From 4f114f38178aeaffcc571aa42e2f930ca916f79d Mon Sep 17 00:00:00 2001 From: Ashley Whetter Date: Sun, 8 May 2016 17:28:28 +0100 Subject: libmakepkg: extract functions for integrity checking Signed-off-by: Ashley Whetter Signed-off-by: Allan McRae --- scripts/libmakepkg/.gitignore | 2 + scripts/libmakepkg/integrity.sh.in | 45 +++++ .../libmakepkg/integrity/generate_checksum.sh.in | 107 ++++++++++ .../libmakepkg/integrity/generate_signature.sh.in | 49 +++++ scripts/libmakepkg/integrity/verify_checksum.sh.in | 130 +++++++++++++ .../libmakepkg/integrity/verify_signature.sh.in | 216 +++++++++++++++++++++ scripts/libmakepkg/util/pkgbuild.sh.in | 20 +- 7 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 scripts/libmakepkg/integrity.sh.in create mode 100644 scripts/libmakepkg/integrity/generate_checksum.sh.in create mode 100644 scripts/libmakepkg/integrity/generate_signature.sh.in create mode 100644 scripts/libmakepkg/integrity/verify_checksum.sh.in create mode 100644 scripts/libmakepkg/integrity/verify_signature.sh.in (limited to 'scripts/libmakepkg') diff --git a/scripts/libmakepkg/.gitignore b/scripts/libmakepkg/.gitignore index 211a0882..941d39a5 100644 --- a/scripts/libmakepkg/.gitignore +++ b/scripts/libmakepkg/.gitignore @@ -1,3 +1,5 @@ +integrity.sh +integrity/*.sh lint_package.sh lint_package/*.sh lint_pkgbuild.sh diff --git a/scripts/libmakepkg/integrity.sh.in b/scripts/libmakepkg/integrity.sh.in new file mode 100644 index 00000000..cb8159d3 --- /dev/null +++ b/scripts/libmakepkg/integrity.sh.in @@ -0,0 +1,45 @@ +#!/bin/bash +# +# integrity.sh - functions relating to source integrity checking +# +# Copyright (c) 2011-2016 Pacman Development Team +# +# 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 . +# + +[[ -n "$LIBMAKEPKG_INTEGRITY_SH" ]] && return +LIBMAKEPKG_INTEGRITY_SH=1 + +LIBRARY=${LIBRARY:-'@libmakepkgdir@'} + +source "$LIBRARY/util/message.sh" + +for lib in "$LIBRARY/integrity/"*.sh; do + source "$lib" +done + +check_source_integrity() { + if (( SKIPCHECKSUMS && SKIPPGPCHECK )); then + warning "$(gettext "Skipping all source file integrity checks.")" + elif (( SKIPCHECKSUMS )); then + warning "$(gettext "Skipping verification of source file checksums.")" + check_pgpsigs "$@" + elif (( SKIPPGPCHECK )); then + warning "$(gettext "Skipping verification of source file PGP signatures.")" + check_checksums "$@" + else + check_checksums "$@" + check_pgpsigs "$@" + fi +} diff --git a/scripts/libmakepkg/integrity/generate_checksum.sh.in b/scripts/libmakepkg/integrity/generate_checksum.sh.in new file mode 100644 index 00000000..7a567100 --- /dev/null +++ b/scripts/libmakepkg/integrity/generate_checksum.sh.in @@ -0,0 +1,107 @@ +#!/bin/bash +# +# generate_checksum.sh - functions for generating source checksums +# +# Copyright (c) 2014-2016 Pacman Development Team +# +# 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 . +# + +[[ -n "$LIBMAKEPKG_INTEGRITY_GENERATE_CHECKSUM_SH" ]] && return +LIBMAKEPKG_INTEGRITY_GENERATE_CHECKSUM_SH=1 + +LIBRARY=${LIBRARY:-'@libmakepkgdir@'} + +source "$LIBRARY/util/message.sh" +source "$LIBRARY/util/pkgbuild.sh" + +generate_one_checksum() { + local integ=$1 arch=$2 sources numsrc indentsz idx + + if [[ $arch ]]; then + array_build sources "source_$arch" + else + array_build sources 'source' + fi + + numsrc=${#sources[*]} + if (( numsrc == 0 )); then + return + fi + + if [[ $arch ]]; then + printf "%ssums_%s=(%n" "$integ" "$arch" indentsz + else + printf "%ssums=(%n" "$integ" indentsz + fi + + for (( idx = 0; idx < numsrc; ++idx )); do + local netfile=${sources[idx]} + local proto sum + proto="$(get_protocol "$netfile")" + + case $proto in + bzr*|git*|hg*|svn*) + sum="SKIP" + ;; + *) + if [[ $netfile != *.@(sig?(n)|asc) ]]; then + local file + file="$(get_filepath "$netfile")" || missing_source_file "$netfile" + sum="$(openssl dgst -${integ} "$file")" + sum=${sum##* } + else + sum="SKIP" + fi + ;; + esac + + # indent checksum on lines after the first + printf "%*s%s" $(( idx ? indentsz : 0 )) '' "'$sum'" + + # print a newline on lines before the last + (( idx < (numsrc - 1) )) && echo + done + + echo ")" +} + +generate_checksums() { + msg "$(gettext "Generating checksums for source files...")" + + if ! type -p openssl >/dev/null; then + error "$(gettext "Cannot find the %s binary required for generating sourcefile checksums.")" "openssl" + exit 1 # $E_MISSING_PROGRAM + fi + + local integlist + if (( $# == 0 )); then + IFS=$'\n' read -rd '' -a integlist < <(get_integlist) + else + integlist=("$@") + fi + + local integ + for integ in "${integlist[@]}"; do + if ! in_array "$integ" "${known_hash_algos[@]}"; then + error "$(gettext "Invalid integrity algorithm '%s' specified.")" "$integ" + exit 1 # $E_CONFIG_ERROR + fi + + generate_one_checksum "$integ" + for a in "${arch[@]}"; do + generate_one_checksum "$integ" "$a" + done + done +} diff --git a/scripts/libmakepkg/integrity/generate_signature.sh.in b/scripts/libmakepkg/integrity/generate_signature.sh.in new file mode 100644 index 00000000..d7f7cb14 --- /dev/null +++ b/scripts/libmakepkg/integrity/generate_signature.sh.in @@ -0,0 +1,49 @@ +#!/bin/bash +# +# generate_signature.sh - functions for generating PGP signatures +# +# Copyright (c) 2008-2016 Pacman Development Team +# +# 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 . +# + +[[ -n "$LIBMAKEPKG_INTEGRITY_GENERATE_SIGNATURE_SH" ]] && return +LIBMAKEPKG_INTEGRITY_GENERATE_SIGNATURE_SH=1 + +LIBRARY=${LIBRARY:-'@libmakepkgdir@'} + +source "$LIBRARY/util/message.sh" + +create_signature() { + if [[ $SIGNPKG != 'y' ]]; then + return + fi + local ret=0 + local filename="$1" + msg "$(gettext "Signing package...")" + + local SIGNWITHKEY="" + if [[ -n $GPGKEY ]]; then + SIGNWITHKEY="-u ${GPGKEY}" + fi + + gpg --detach-sign --use-agent ${SIGNWITHKEY} --no-armor "$filename" &>/dev/null || ret=$? + + + if (( ! ret )); then + msg2 "$(gettext "Created signature file %s.")" "$filename.sig" + else + warning "$(gettext "Failed to sign package file.")" + fi +} diff --git a/scripts/libmakepkg/integrity/verify_checksum.sh.in b/scripts/libmakepkg/integrity/verify_checksum.sh.in new file mode 100644 index 00000000..44a2b2e1 --- /dev/null +++ b/scripts/libmakepkg/integrity/verify_checksum.sh.in @@ -0,0 +1,130 @@ +#!/bin/bash +# +# verify_checksum.sh - functions for checking source checksums +# +# Copyright (c) 2014-2016 Pacman Development Team +# +# 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 . +# + +[[ -n "$LIBMAKEPKG_INTEGRITY_VERIFY_CHECKSUM_SH" ]] && return +LIBMAKEPKG_INTEGRITY_CHECKSUM_SH=1 + +LIBRARY=${LIBRARY:-'@libmakepkgdir@'} + +source "$LIBRARY/util/message.sh" +source "$LIBRARY/util/pkgbuild.sh" + +check_checksums() { + local integ a + declare -A correlation + (( SKIPCHECKSUMS )) && return 0 + + # Initialize a map which we'll use to verify that every source array has at + # least some kind of checksum array associated with it. + (( ${#source[*]} )) && correlation['source']=1 + case $1 in + all) + for a in "${arch[@]}"; do + array_build _ source_"$a" && correlation["source_$a"]=1 + done + ;; + *) + array_build _ source_"$CARCH" && correlation["source_$CARCH"]=1 + ;; + esac + + for integ in "${known_hash_algos[@]}"; do + verify_integrity_sums "$integ" && unset "correlation[source]" + + case $1 in + all) + for a in "${arch[@]}"; do + verify_integrity_sums "$integ" "$a" && unset "correlation[source_$a]" + done + ;; + *) + verify_integrity_sums "$integ" "$CARCH" && unset "correlation[source_$CARCH]" + ;; + esac + done + + if (( ${#correlation[*]} )); then + error "$(gettext "Integrity checks are missing for: %s")" "${!correlation[*]}" + exit 1 # TODO: error code + fi +} + +verify_integrity_one() { + local source_name=$1 integ=$2 expectedsum=$3 + + local file="$(get_filename "$source_name")" + printf ' %s ... ' "$file" >&2 + + if [[ $expectedsum = 'SKIP' ]]; then + printf '%s\n' "$(gettext "Skipped")" >&2 + return + fi + + if ! file="$(get_filepath "$file")"; then + printf '%s\n' "$(gettext "NOT FOUND")" >&2 + return 1 + fi + + local realsum="$(openssl dgst -${integ} "$file")" + realsum="${realsum##* }" + if [[ ${expectedsum,,} = "$realsum" ]]; then + printf '%s\n' "$(gettext "Passed")" >&2 + else + printf '%s\n' "$(gettext "FAILED")" >&2 + return 1 + fi + + return 0 +} + +verify_integrity_sums() { + local integ=$1 arch=$2 integrity_sums=() sources=() srcname + + if [[ $arch ]]; then + array_build integrity_sums "${integ}sums_$arch" + srcname=source_$arch + else + array_build integrity_sums "${integ}sums" + srcname=source + fi + + array_build sources "$srcname" + if (( ${#integrity_sums[@]} == 0 && ${#sources[@]} == 0 )); then + return 1 + fi + + if (( ${#integrity_sums[@]} == ${#sources[@]} )); then + msg "$(gettext "Validating %s files with %s...")" "$srcname" "${integ}sums" + local idx errors=0 + for (( idx = 0; idx < ${#sources[*]}; idx++ )); do + verify_integrity_one "${sources[idx]}" "$integ" "${integrity_sums[idx]}" || errors=1 + done + + if (( errors )); then + error "$(gettext "One or more files did not pass the validity check!")" + exit 1 # TODO: error code + fi + elif (( ${#integrity_sums[@]} )); then + error "$(gettext "Integrity checks (%s) differ in size from the source array.")" "$integ" + exit 1 # TODO: error code + else + return 1 + fi +} diff --git a/scripts/libmakepkg/integrity/verify_signature.sh.in b/scripts/libmakepkg/integrity/verify_signature.sh.in new file mode 100644 index 00000000..6df62727 --- /dev/null +++ b/scripts/libmakepkg/integrity/verify_signature.sh.in @@ -0,0 +1,216 @@ +#!/bin/bash +# +# verify_signature.sh - functions for checking PGP signatures +# +# Copyright (c) 2011-2016 Pacman Development Team +# +# 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 . +# + +[[ -n "$LIBMAKEPKG_INTEGRITY_VERIFY_SIGNATURE_SH" ]] && return +LIBMAKEPKG_INTEGRITY_VERIFY_SIGNATURE_SH=1 + +LIBRARY=${LIBRARY:-'@libmakepkgdir@'} + +source "$LIBRARY/util/message.sh" +source "$LIBRARY/util/pkgbuild.sh" + +check_pgpsigs() { + (( SKIPPGPCHECK )) && return 0 + ! source_has_signatures && return 0 + + msg "$(gettext "Verifying source file signatures with %s...")" "gpg" + + local file ext decompress found pubkey success status fingerprint trusted + local warning=0 + local errors=0 + local statusfile=$(mktemp) + local all_sources + + case $1 in + all) + get_all_sources 'all_sources' + ;; + *) + get_all_sources_for_arch 'all_sources' + ;; + esac + for file in "${all_sources[@]}"; do + file="$(get_filename "$file")" + if [[ $file != *.@(sig?(n)|asc) ]]; then + continue + fi + + printf " %s ... " "${file%.*}" >&2 + + if ! file="$(get_filepath "$file")"; then + printf '%s\n' "$(gettext "SIGNATURE NOT FOUND")" >&2 + errors=1 + continue + fi + + found=0 + for ext in "" gz bz2 xz lrz lzo Z; do + if sourcefile="$(get_filepath "${file%.*}${ext:+.$ext}")"; then + found=1 + break; + fi + done + if (( ! found )); then + printf '%s\n' "$(gettext "SOURCE FILE NOT FOUND")" >&2 + errors=1 + continue + 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" ;; + Z) decompress="uncompress -c -f" ;; + "") decompress="cat" ;; + esac + + $decompress < "$sourcefile" | gpg --quiet --batch --status-file "$statusfile" --verify "$file" - 2> /dev/null + # 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 + printf "%s (%s %s)" "$(gettext "FAILED")" "$(gettext "invalid public key")" "$fingerprint" + 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 + + rm -f "$statusfile" + + 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.")" + plain "$(gettext "Please make sure you really trust them.")" + fi +} + +parse_gpg_statusfile() { + local type arg1 arg6 arg10 + + 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() { + local file all_sources + + get_all_sources_for_arch 'all_sources' + for file in "${all_sources[@]}"; do + if [[ ${file%%::*} = *.@(sig?(n)|asc) ]]; then + return 0 + fi + done + return 1 +} diff --git a/scripts/libmakepkg/util/pkgbuild.sh.in b/scripts/libmakepkg/util/pkgbuild.sh.in index 51d87329..1a4567dc 100644 --- a/scripts/libmakepkg/util/pkgbuild.sh.in +++ b/scripts/libmakepkg/util/pkgbuild.sh.in @@ -2,7 +2,7 @@ # # pkgbuild.sh - functions to extract information from PKGBUILD files # -# Copyright (c) 2014-2016 Pacman Development Team +# Copyright (c) 2009-2016 Pacman Development Team # # 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 @@ -189,3 +189,21 @@ get_all_sources_for_arch() { array_build "$1" "aggregate" } + +get_integlist() { + local integ + local integlist=() + + for integ in "${known_hash_algos[@]}"; do + local sumname="${integ}sums[@]" + if [[ -n ${!sumname} ]]; then + integlist+=("$integ") + fi + done + + if (( ${#integlist[@]} > 0 )); then + printf "%s\n" "${integlist[@]}" + else + printf "%s\n" "${INTEGRITY_CHECK[@]}" + fi +} -- cgit v1.2.3-70-g09d2