2016-05-08 17:28:28 +01:00
|
|
|
#!/bin/bash
|
|
|
|
#
|
|
|
|
# verify_signature.sh - functions for checking PGP signatures
|
|
|
|
#
|
2022-01-02 13:34:52 +10:00
|
|
|
# Copyright (c) 2011-2022 Pacman Development Team <pacman-dev@lists.archlinux.org>
|
2016-05-08 17:28:28 +01:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#
|
|
|
|
|
|
|
|
[[ -n "$LIBMAKEPKG_INTEGRITY_VERIFY_SIGNATURE_SH" ]] && return
|
|
|
|
LIBMAKEPKG_INTEGRITY_VERIFY_SIGNATURE_SH=1
|
|
|
|
|
2023-10-21 01:45:20 +01:00
|
|
|
MAKEPKG_LIBRARY=${MAKEPKG_LIBRARY:-'@libmakepkgdir@'}
|
2016-05-08 17:28:28 +01:00
|
|
|
|
2023-10-21 01:45:20 +01:00
|
|
|
source "$MAKEPKG_LIBRARY/util/message.sh"
|
|
|
|
source "$MAKEPKG_LIBRARY/util/pkgbuild.sh"
|
2016-05-08 17:28:28 +01:00
|
|
|
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
# Filter the contents of a GnuPG statusfile to only contain understood terms to narrow the file's scope and circumvent
|
|
|
|
# the use of terms (e.g. NOTATION_DATA) that may contain unescaped binary data
|
|
|
|
filter_gnupg_statusfile() {
|
|
|
|
grep -E "(.*SIG| TRUST_.*)"
|
|
|
|
}
|
|
|
|
|
2016-05-08 17:28:28 +01:00
|
|
|
check_pgpsigs() {
|
|
|
|
(( SKIPPGPCHECK )) && return 0
|
|
|
|
! source_has_signatures && return 0
|
|
|
|
|
|
|
|
msg "$(gettext "Verifying source file signatures with %s...")" "gpg"
|
|
|
|
|
2017-01-03 15:10:18 -05:00
|
|
|
local netfile proto pubkey success status fingerprint trusted
|
2017-07-05 23:14:14 -04:00
|
|
|
local warnings=0
|
2016-05-08 17:28:28 +01:00
|
|
|
local errors=0
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
local statusfile_raw="$(mktemp)"
|
2016-05-08 17:28:28 +01:00
|
|
|
local statusfile=$(mktemp)
|
|
|
|
local all_sources
|
|
|
|
|
|
|
|
case $1 in
|
|
|
|
all)
|
|
|
|
get_all_sources 'all_sources'
|
|
|
|
;;
|
|
|
|
*)
|
|
|
|
get_all_sources_for_arch 'all_sources'
|
|
|
|
;;
|
|
|
|
esac
|
2017-01-03 15:10:17 -05:00
|
|
|
for netfile in "${all_sources[@]}"; do
|
2017-01-03 15:10:18 -05:00
|
|
|
proto="$(get_protocol "$netfile")"
|
|
|
|
|
2018-05-29 00:24:35 -04:00
|
|
|
if declare -f verify_${proto}_signature > /dev/null; then
|
|
|
|
verify_${proto}_signature "$netfile" "$statusfile" || continue
|
2017-01-03 15:10:18 -05:00
|
|
|
else
|
|
|
|
verify_file_signature "$netfile" "$statusfile" || continue
|
|
|
|
fi
|
2016-05-08 17:28:28 +01:00
|
|
|
|
|
|
|
# these variables are assigned values in parse_gpg_statusfile
|
|
|
|
success=0
|
|
|
|
status=
|
|
|
|
pubkey=
|
|
|
|
fingerprint=
|
|
|
|
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
|
|
|
|
errors=1
|
|
|
|
else
|
|
|
|
if (( ${#validpgpkeys[@]} == 0 && !trusted )); then
|
|
|
|
printf "%s ($(gettext "the public key %s is not trusted"))" $(gettext "FAILED") "$fingerprint" >&2
|
|
|
|
errors=1
|
|
|
|
elif (( ${#validpgpkeys[@]} > 0 )) && ! in_array "$fingerprint" "${validpgpkeys[@]}"; then
|
2017-07-03 23:13:23 -04:00
|
|
|
printf "%s (%s %s)" "$(gettext "FAILED")" "$(gettext "invalid public key")" "$fingerprint" >&2
|
2016-05-08 17:28:28 +01:00
|
|
|
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
|
|
|
|
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
rm -f "$statusfile" "$statusfile_raw"
|
2016-05-08 17:28:28 +01:00
|
|
|
|
|
|
|
if (( errors )); then
|
|
|
|
error "$(gettext "One or more PGP signatures could not be verified!")"
|
|
|
|
exit 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
if (( warnings )); then
|
|
|
|
warning "$(gettext "Warnings have occurred while verifying the signatures.")"
|
2020-06-02 17:50:24 -04:00
|
|
|
plainerr "$(gettext "Please make sure you really trust them.")"
|
2016-05-08 17:28:28 +01:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2017-01-03 15:10:17 -05:00
|
|
|
verify_file_signature() {
|
|
|
|
local netfile="$1" statusfile="$2"
|
|
|
|
local file ext decompress found sourcefile
|
|
|
|
|
|
|
|
file="$(get_filename "$netfile")"
|
|
|
|
if [[ $file != *.@(sig?(n)|asc) ]]; then
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
printf " %s ... " "${file%.*}" >&2
|
|
|
|
|
|
|
|
if ! file="$(get_filepath "$netfile")"; then
|
|
|
|
printf '%s\n' "$(gettext "SIGNATURE NOT FOUND")" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
found=0
|
2021-02-21 22:36:31 +01:00
|
|
|
for ext in "" gz bz2 xz lrz lzo Z zst; do
|
2017-01-03 15:10:17 -05:00
|
|
|
if sourcefile="$(get_filepath "${file%.*}${ext:+.$ext}")"; then
|
|
|
|
found=1
|
2018-06-19 22:33:57 +02:00
|
|
|
break
|
2017-01-03 15:10:17 -05:00
|
|
|
fi
|
|
|
|
done
|
|
|
|
if (( ! found )); then
|
|
|
|
printf '%s\n' "$(gettext "SOURCE FILE NOT FOUND")" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
case "$ext" in
|
|
|
|
gz) decompress="gzip -c -d -f" ;;
|
|
|
|
bz2) decompress="bzip2 -c -d -f" ;;
|
|
|
|
xz) decompress="xz -c -d" ;;
|
|
|
|
lrz) decompress="lrzip -q -d" ;;
|
|
|
|
lzo) decompress="lzop -c -d -q" ;;
|
2021-02-21 22:36:31 +01:00
|
|
|
zst) decompress="zstd -d -q -f" ;;
|
2017-01-03 15:10:17 -05:00
|
|
|
Z) decompress="uncompress -c -f" ;;
|
|
|
|
"") decompress="cat" ;;
|
|
|
|
esac
|
|
|
|
|
2024-01-22 13:48:15 +01:00
|
|
|
# verify the signature and write metadata to a status file
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
if ! $decompress < "$sourcefile" | gpg --quiet --batch --status-file "$statusfile_raw" --verify "$file" - 2> /dev/null; then
|
2024-01-22 13:48:15 +01:00
|
|
|
printf '%s\n' "$(gettext "%s is unable to verify the signature.")" "gpg" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
# create a statusfile that contains only understood terms
|
|
|
|
if ! filter_gnupg_statusfile > "$statusfile" < "$statusfile_raw"; then
|
|
|
|
printf '%s\n' "$(gettext "unable to extract signature metadata.")" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
2017-11-21 23:34:46 -05:00
|
|
|
return 0
|
2017-01-03 15:10:17 -05:00
|
|
|
}
|
|
|
|
|
2017-01-03 15:10:18 -05:00
|
|
|
verify_git_signature() {
|
|
|
|
local netfile=$1 statusfile=$2
|
|
|
|
local dir fragment query fragtype fragval
|
|
|
|
|
|
|
|
dir=$(get_filepath "$netfile")
|
|
|
|
fragment=$(get_uri_fragment "$netfile")
|
|
|
|
query=$(get_uri_query "$netfile")
|
|
|
|
|
|
|
|
if [[ $query != signed ]]; then
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
|
|
|
case ${fragment%%=*} in
|
|
|
|
tag)
|
|
|
|
fragtype=tag
|
|
|
|
fragval=${fragment##*=}
|
|
|
|
;;
|
|
|
|
commit|branch)
|
|
|
|
fragtype=commit
|
|
|
|
fragval=${fragment##*=}
|
|
|
|
;;
|
|
|
|
'')
|
|
|
|
fragtype=commit
|
|
|
|
fragval=HEAD
|
|
|
|
esac
|
|
|
|
|
|
|
|
printf " %s git repo ... " "${dir##*/}" >&2
|
|
|
|
|
2024-01-22 13:48:15 +01:00
|
|
|
# verify the signature and write metadata to a status file
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
if ! git -C "$dir" verify-$fragtype --raw "$fragval" > "$statusfile_raw" 2>&1; then
|
2024-01-22 13:48:15 +01:00
|
|
|
printf '%s\n' "$(gettext "%s is unable to verify the signature.")" "git" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
makepkg: Improve robustness of signature verification by limiting terms
The output of
`gpg --quiet --batch --status-fd /dev/stdout --verify <signature_file> <file> 2> /dev/null`
or
`git verify-commit --raw <commit> 2>&1`
may contain binary data, if the signature has been created with an
OpenPGP implementation, that e.g. makes use of notations.
If the notation string (see `NOTATION_DATA` in /usr/share/doc/gnupg/
DETAILS) contains a trailing binary char, this will break signature
verification, as any following entry (e.g. `VALIDSIG`) will be offset.
As we are only making use of a narrow set of terms from the statusfile
(namely `NEWSIG`, `GOODSIG`, `EXPSIG`, `EXPKEYSIG`, `REVKEYSIG`,
`BADSIG`, `ERRSIG`, `VALIDSIG`, `TRUST_UNDEFINED`, `TRUST_NEVER`,
`TRUST_MARGINAL`, `TRUST_FULLY`, `TRUST_ULTIMATE`), we are applying a
filter, so that only understood terms are written to the file.
Signed-off-by: David Runge <dvzrv@archlinux.org>
2024-01-21 12:33:04 +01:00
|
|
|
# create a statusfile that contains only understood terms
|
|
|
|
if ! filter_gnupg_statusfile > "$statusfile" < "$statusfile_raw"; then
|
|
|
|
printf '%s\n' "$(gettext "unable to extract signature metadata.")" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
2017-11-21 23:34:46 -05:00
|
|
|
return 0
|
2017-01-03 15:10:18 -05:00
|
|
|
}
|
|
|
|
|
2016-05-08 17:28:28 +01:00
|
|
|
parse_gpg_statusfile() {
|
|
|
|
local type arg1 arg6 arg10
|
|
|
|
|
2024-01-22 14:04:28 +01:00
|
|
|
# ensure the NEWSIG keyword is part of the metadata
|
|
|
|
if ! grep -qs NEWSIG "$statusfile"; then
|
|
|
|
printf '%s\n' "$(gettext "SIGNATURE NOT FOUND")" >&2
|
|
|
|
errors=1
|
|
|
|
return 1
|
|
|
|
fi
|
|
|
|
|
2016-05-08 17:28:28 +01:00
|
|
|
while read -r _ type arg1 _ _ _ _ arg6 _ _ _ arg10 _; 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
|
|
|
|
;;
|
|
|
|
VALIDSIG)
|
|
|
|
if [[ $arg10 ]]; then
|
|
|
|
# If the file was signed with a subkey, arg10 contains
|
|
|
|
# the fingerprint of the primary key
|
|
|
|
fingerprint=$arg10
|
|
|
|
else
|
|
|
|
fingerprint=$arg1
|
|
|
|
fi
|
|
|
|
;;
|
|
|
|
TRUST_UNDEFINED|TRUST_NEVER)
|
|
|
|
trusted=0
|
|
|
|
;;
|
|
|
|
TRUST_MARGINAL|TRUST_FULLY|TRUST_ULTIMATE)
|
|
|
|
trusted=1
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
done < "$1"
|
|
|
|
}
|
|
|
|
|
|
|
|
source_has_signatures() {
|
2017-11-21 23:34:47 -05:00
|
|
|
local netfile all_sources proto
|
2016-05-08 17:28:28 +01:00
|
|
|
|
|
|
|
get_all_sources_for_arch 'all_sources'
|
2017-11-21 23:34:47 -05:00
|
|
|
for netfile in "${all_sources[@]}"; do
|
|
|
|
proto="$(get_protocol "$netfile")"
|
2017-01-03 15:10:18 -05:00
|
|
|
query=$(get_uri_query "$netfile")
|
|
|
|
|
2018-05-29 00:24:35 -04:00
|
|
|
if [[ ${netfile%%::*} = *.@(sig?(n)|asc) ]] || \
|
|
|
|
( declare -f verify_${proto}_signature > /dev/null && [[ $query = signed ]] ); then
|
2016-05-08 17:28:28 +01:00
|
|
|
return 0
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
return 1
|
|
|
|
}
|