Add an ability to verify the digest/signature

Fixes #611.

Signed-off-by: Alexey Neyman <stilor@att.net>
This commit is contained in:
Alexey Neyman 2017-09-27 22:29:35 -07:00
parent f86adab1f4
commit 48a949cf60
27 changed files with 280 additions and 48 deletions

3
TODO
View File

@ -2,7 +2,8 @@ A (slightly) ordered set of tasks for crosstool-NG. Written in a cryptic languag
-- Alexey Neyman (@stilor)
[ ] mingw-w64 fails to build (with new gcc?) - the headers are installed into usr/x86_64-w64-mingw32/x86_64-w64-mingw32/include instead of usr/x86_64-w64-mingw32/include
[ ] mirror: remove crosstool-ng.org mirroring of archives? Use the option only for local mirrors? Archives currently hosted are outdated.
[ ] old mingw-w64 fails to build (with new gcc?) - the headers are installed into usr/x86_64-w64-mingw32/x86_64-w64-mingw32/include instead of usr/x86_64-w64-mingw32/include
[ ] fix or retire?
[ ] test-packages.sh
[ ] FTP URLs always succeed in verification (wget bug) - how to work around?

View File

@ -542,6 +542,7 @@ enter_fork()
info[archive_dirname]='@{pkg_name}-@{version}'
info[versionlocked]=
info[origin]=
info[signature_format]=
eval `read_package_desc ${fork}`

View File

@ -132,4 +132,49 @@ config MIRROR_BASE_URL
endif # USE_MIRROR
config VERIFY_DOWNLOAD_DIGEST
bool "Verify download digests (checksums)"
default y
if VERIFY_DOWNLOAD_DIGEST
choice
prompt "Digest algorithm"
config VERIFY_DOWNLOAD_DIGEST_SHA512
bool "SHA-512"
config VERIFY_DOWNLOAD_DIGEST_SHA256
bool "SHA-256"
config VERIFY_DOWNLOAD_DIGEST_SHA1
bool "SHA-1"
config VERIFY_DOWNLOAD_DIGEST_MD5
bool "MD5"
endchoice
config VERIFY_DOWNLOAD_DIGEST_ALG
string
default "md5" if VERIFY_DOWNLOAD_DIGEST_MD5
default "sha1" if VERIFY_DOWNLOAD_DIGEST_SHA1
default "sha256" if VERIFY_DOWNLOAD_DIGEST_SHA256
default "sha512" if VERIFY_DOWNLOAD_DIGEST_SHA512
endif # VERIFY_DOWNLOAD_DIGEST
config VERIFY_DOWNLOAD_SIGNATURE
bool "Verify detached signatures"
help
Verify the signatures for the downloads where available.
This requires the public keys for all the packages used in the build
to be imported into a keyring on the build machine.
Note that the validation will succeed so long as the signature
is known and valid, even if the signature is not trusted! GnuPG
issues a warning in this case, but ultimately reports that validation
succeeded. Check the log file if this is a concern; crosstool-NG
may add an option to verify this more paranoidally later.
endif # ! FORBID_DOWNLOAD

View File

@ -274,6 +274,13 @@ config @@fork|@@_ARCHIVE_FORMATS
#!end-foreach
default "@@archive_formats@@"
config @@fork|@@_SIGNATURE_FORMAT
string
#!foreach version if-differs signature_format
default "@@signature_format@@" if @@fork|@@_V_@@ver_sel|@@
#!end-foreach
default "@@signature_format@@"
#!end-if
#!if [ "@@nforks@@" -ge 2 ]

View File

@ -18,6 +18,9 @@ Options:
--download, -d
Download all packages to the default directory (\$HOME/src).
--digest, -D
Create digests (MD5/SHA-1/...) for all package archives.
--apply-patches, -a
Implies -d. Unpack and apply the bundled patches.
@ -37,6 +40,9 @@ while [ -n "${1}" ]; do
--download|-d)
download_pkgs=y
;;
--digest|-D)
create_digests=y
;;
--verify-urls|-u)
verify_urls=y
;;
@ -61,7 +67,7 @@ while [ -n "${1}" ]; do
shift
done
if [ -z "${download_pkgs}${verify_urls}" ]; then
if [ -z "${download_pkgs}${create_digests}${verify_urls}" ]; then
echo "No action selected" >&2
exit 1
fi
@ -124,6 +130,30 @@ check_pkg_urls()
done
}
create_digests()
{
local e m url alg
local save_archive_formats="${archive_formats}"
for e in ${save_archive_formats}; do
CT_DoStep EXTRA "Downloading ${archive_filename}${e}"
archive_formats="${e}"
CT_DoFetch
CT_Pushd "${CT_LOCAL_TARBALLS_DIR}"
for alg in md5 sha1 sha256 sha512; do
CT_DoLog EXTRA "Creating ${alg^^} digest for ${archive_filename}${e}"
if ! CT_DoExecLog ALL ${alg}sum "${archive_filename}${e}" > \
"${CT_LIB_DIR}/packages/${pkg_name}/${version}/${archive_filename}${e}.${alg}"; then
CT_DoExecLog ALL rm -f "${CT_LIB_DIR}/packages/${pkg_name}/${version}/${archive_filename}${e}.${alg}"
CT_Abort "${alg}sum failed"
fi
done
CT_Popd
CT_EndStep
done
archive_formats="${save_archive_formats}"
}
run_pkgversion()
{
while [ -n "${1}" ]; do
@ -171,6 +201,7 @@ CT_${masterpfx}_USE_${originpfx}=y
CT_${pfx}_SRC_RELEASE=y
CT_${pfx}_V_${kcfg}=y
CT_SAVE_TARBALLS=y
# CT_VERIFY_DOWNLOAD_DIGEST is not set
EOF
./kconfig/conf --defconfig=temp.defconfig temp.in >/dev/null
@ -181,6 +212,10 @@ EOF
CT_DoLog EXTRA "Verifying URLs for ${pkg_name}-${ver}"
CT_PackageRun "${masterpfx}" check_pkg_urls
fi
if [ -n "${create_digests}" ]; then
CT_DoLog EXTRA "Creating digests for ${pkg_name}-${ver}"
CT_PackageRun "${masterpfx}" create_digests
fi
if [ -n "${download_pkgs}" ]; then
CT_DoLog EXTRA "Downloading ${pkg_name}-${ver}"
CT_Fetch "${masterpfx}"

View File

@ -2,3 +2,4 @@ repository='git git://git.sv.gnu.org/autoconf'
bootstrap='autoreconf -vi'
mirrors='$(CT_Mirrors GNU autoconf)'
archive_formats='.tar.xz .tar.gz'
signature_format='packed/.sig'

View File

@ -2,3 +2,4 @@ repository='git https://git.savannah.gnu.org/git/automake.git'
bootstrap='./bootstrap'
mirrors='$(CT_Mirrors GNU automake)'
archive_formats='.tar.xz .tar.gz'
signature_format='packed/.sig'

View File

@ -1,3 +1,4 @@
repository='svn svn://svn.savannah.nongnu.org/avr-libc/trunk/avr-libc'
mirrors='http://download.savannah.gnu.org/releases/avr-libc'
archive_formats='.tar.bz2'
signature_format='packed/.sig'

View File

@ -3,3 +3,4 @@ mirrors='$(CT_Mirrors GNU binutils) $(CT_Mirrors sourceware binutils/releases)'
origin='GNU'
milestones='2.23 2.25'
archive_formats='.tar.xz .tar.bz2 .tar.gz'
signature_format='packed/.sig'

View File

@ -4,3 +4,4 @@ repository='git https://git.savannah.gnu.org/git/gettext.git'
bootstrap='./autogen.sh'
mirrors='$(CT_Mirrors GNU gettext)'
archive_formats='.tar.xz .tar.lz .tar.gz'
signature_format='packed/.sig'

View File

@ -8,3 +8,4 @@ mirrors='$(CT_Mirrors GNU glibc)'
versionlocked='glibc'
archive_formats='.tar.xz .tar.bz2 .tar.gz'
signature_format='packed/.sig'

View File

@ -3,3 +3,4 @@ repository='git git://sourceware.org/git/glibc.git'
mirrors='$(CT_Mirrors GNU glibc)'
milestones='2.14 2.17 2.20 2.23 2.24 2.26'
archive_formats='.tar.xz .tar.bz2 .tar.gz'
signature_format='packed/.sig'

View File

@ -3,3 +3,4 @@ bootstrap='./.bootstrap'
mirrors='https://gmplib.org/download/gmp https://gmplib.org/download/gmp/archive $(CT_Mirrors GNU gmp)'
milestones='5.1'
archive_formats='.tar.xz .tar.lz .tar.bz2'
signature_format='packed/.sig'

View File

@ -4,3 +4,4 @@ repository='git https://git.savannah.gnu.org/git/libiconv.git'
bootstrap='./autogen.sh'
mirrors='$(CT_Mirrors GNU libiconv)'
archive_formats='.tar.gz'
signature_format='packed/.sig'

View File

@ -2,3 +2,4 @@ repository='git git://git.savannah.gnu.org/libtool.git'
bootstrap='./bootstrap'
mirrors='$(CT_Mirrors GNU libtool)'
archive_formats='.tar.xz .tar.gz'
signature_format='packed/.sig'

View File

@ -3,3 +3,4 @@ mirrors='$(CT_Mirrors kernel.org linux ${CT_LINUX_VERSION})'
relevantpattern='2.*.*|. *.*|.'
milestones='3.2'
archive_formats='.tar.xz .tar.gz'
signature_format='unpacked/.sign'

View File

@ -2,3 +2,4 @@ repository='git git://git.sv.gnu.org/m4'
bootstrap='./bootstrap'
mirrors='$(CT_Mirrors GNU m4)'
archive_formats='.tar.xz .tar.bz2 .tar.gz'
signature_format='packed/.sig'

View File

@ -2,3 +2,4 @@ repository='git https://git.savannah.gnu.org/git/make.git'
bootstrap='autoreconf -i'
mirrors='$(CT_Mirrors GNU make)'
archive_formats='.tar.bz2 .tar.gz'
signature_format='packed/.sig'

View File

@ -2,3 +2,4 @@ repository='git https://git.code.sf.net/p/mingw-w64/mingw-w64'
mirrors='http://downloads.sourceforge.net/sourceforge/mingw-w64 https://downloads.sourceforge.net/project/mingw-w64/mingw-w64/mingw-w64-release/'
milestones='v4'
archive_formats='.tar.bz2 .zip'
signature_format='packed/.sig'

View File

@ -1,6 +1,5 @@
repository='git https://scm.gforge.inria.fr/anonscm/git/mpc/mpc.git'
bootstrap='autoreconf -i'
mirrors='http://www.multiprecision.org/mpc/download $(CT_Mirrors GNU mpc)'
archive_formats='.tar.gz'
signature_format='packed/.sig'

View File

@ -3,3 +3,4 @@ repository='svn https://scm.gforge.inria.fr/anonscm/svn/mpfr'
bootstrap='wget -O m4/ax_pthread.m4 \\"http://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_pthread.m4\\" && ./autogen.sh'
mirrors='http://www.mpfr.org/mpfr-${CT_MPFR_VERSION} $(CT_Mirrors GNU mpfr)'
archive_formats='.tar.xz .tar.bz2 .tar.gz .zip'
signature_format='packed/.asc'

View File

@ -1,3 +1,4 @@
repository='git git://git.musl-libc.org/musl'
mirrors='http://www.musl-libc.org/releases'
archive_formats='.tar.gz'
signature_format='packed/.asc'

View File

@ -1,3 +1,4 @@
# No public repository for ncurses
mirrors='ftp://invisible-island.net/ncurses $(CT_Mirrors GNU ncurses)'
archive_formats='.tar.gz'
signature_format='packed/.sig'

View File

@ -3,3 +3,4 @@ origin='uclibc-ng.org'
repository='git git://uclibc-ng.org/git/uclibc-ng'
mirrors='http://downloads.uclibc-ng.org/releases/${CT_UCLIBC_NG_VERSION}'
archive_formats='.tar.xz .tar.lz .tar.bz2 .tar.gz'
signature_format='packed/.asc'

View File

@ -1,3 +1,4 @@
repository='git https://github.com/madler/zlib.git'
mirrors='http://downloads.sourceforge.net/project/libpng/zlib/${CT_ZLIB_VERSION}'
archive_formats='.tar.xz .tar.gz'
signature_format='packed/.asc'

View File

@ -14,7 +14,8 @@ do_cc_get() {
# GCC source tree, which will not be there unless we get it and
# put it there ourselves
if [ "${CT_CC_LANG_JAVA_USE_ECJ}" = "y" ]; then
if ! CT_GetFile ecj ecj-latest .jar $(CT_Mirrors sourceware java); then
if ! CT_GetFile package=ecj basename=ecj-latest extensions=.jar \
mirrors=$(CT_Mirrors sourceware java); then
# Should be a package, too - but with Java retirement in GCC,
# it may not make sense.
CT_Abort "Failed to download ecj-latest.jar"

View File

@ -382,6 +382,7 @@ CT_DoExecLog() {
break
fi
done
CT_DoLog DEBUG "==> Return status ${ret}"
exit ${ret}
)
# Catch failure of the sub-shell
@ -702,7 +703,8 @@ CT_GetFileBasename()
# This functions always returns true (0), as it can be legitimate not
# to find the requested URL (think about snapshots, different layouts
# for different gcc versions, etc...).
CT_DoGetFile() {
CT_DoGetFile()
{
local url="${1}"
local dest="${CT_TARBALLS_DIR}/${url##*/}"
local tmp="${dest}.tmp-dl"
@ -750,7 +752,8 @@ CT_DoGetFile() {
# This function saves the specified to local storage if possible,
# and if so, symlinks it for later usage
# Usage: CT_SaveLocal </full/path/file.name>
CT_SaveLocal() {
CT_SaveLocal()
{
local file="$1"
local basename="${file##*/}"
@ -763,42 +766,160 @@ CT_SaveLocal() {
fi
}
# Verify the file against a known digest.
# Usage: CT_DoVerifyDigest <local-file-path> <package-directory>
CT_DoVerifyDigest()
{
local path="$1"
local file="${path##*/}"
local dir="${path%/*}"
local pkgdir="$2"
local alg="${CT_VERIFY_DOWNLOAD_DIGEST_ALG}"
if [ ! -r "${pkgdir}/${file}.${alg}" ]; then
CT_DoLog WARN "Not verifying '${file}': digest missing"
return
fi
CT_DoLog EXTRA "Verifying ${alg^^} checksum for '${file}'"
CT_Pushd "${dir}"
if ! CT_DoExecLog ALL "${alg}sum" -c "${pkgdir}/${file}.${alg}"; then
CT_Popd
return 1
fi
CT_Popd
}
# Decompress a file to stdout
CT_ZCat()
{
local file="$1"
case "${file}" in
*.tar.xz)
xz -fdc "${file}"
;;
*.tar.lzma)
xz -fdc --format=lzma "${file}"
;;
*.tar.lz)
lzip -fdc "${file}"
;;
*.tar.bz2)
bzip2 -dc "${file}"
;;
*.tar.gz|*.tgz)
gzip -dc "${file}"
;;
*.tar)
cat "${file}"
;;
*)
CT_Abort "Unsupported archive file name '${file}'"
esac
}
# Verify the file against a detached signature.
# Fetched from the URL, or obtained from the package directory.
# Usage: CT_DoVerifySignature <local-file-path> <URL-used-for-download> <signature-format>
CT_DoVerifySignature()
{
local path="$1"
local file="${path##*/}"
local dir="${path%/*}"
local url="$2"
local urldir="${url%/*}"
local format="$3"
local method="${format%/*}"
local ext="${format#*/}"
local sigfile
local cat
case "${method}" in
packed)
# Typical case: release is packed, then signed
sigfile="${file}"
cat=cat
;;
unpacked)
# Linux kernel: uncompressed tarball is signed, them compressed by various methods
case "${file}" in
*.tar.*)
sigfile="${file%.tar.*}.tar"
cat=CT_ZCat
;;
*)
CT_Abort "'unpacked' signature method only supported for tar archives"
;;
esac
;;
*)
CT_Abort "Unsupported signature method ${method}"
;;
esac
# No recursion, as we don't pass signature_format argument
if ! CT_DoGetFile "${urldir}/${sigfile}${ext}"; then
CT_DoLog WARN "Failed to download the signature '${sigfile}${ext}'"
return 1
fi
CT_Pushd "${dir}"
if ! ${cat} "${file}" | CT_DoExecLog ALL gpg --verify "${sigfile}${ext}" -; then
# Remove the signature so it's re-downloaded next time
CT_DoExecLog ALL rm "${sigfile}${ext}"
CT_Popd
return 1
fi
CT_Popd
# If we get here, verification succeeded.
CT_SaveLocal "${CT_TARBALLS_DIR}/${sigfile}${ext}"
}
# Download the file from one of the URLs passed as argument
# Usage: CT_GetFile <packagename> <basename> <extensions> <url> [url ...]
CT_GetFile() {
local ext
CT_GetFile()
{
local -a argnames=(
package # Name of the package
version # Version of the package
basename # Base name of file/archive
extensions # Extension(s) for the file/archive
digest # If 'y', verify the digest
signature_format # Format of the signature
mirrors # Mirrors to download from
)
local -a URLS
local url
local package="$1"
local file="$2"
local extensions="$3"
shift 3
local ext url
for arg in "${argnames[@]/%/=}" "$@"; do
eval "local ${arg//[[:space:]]/\\ }"
done
# Does any of the requested files exist localy?
for ext in ${extensions}; do
# Do we already have it in *our* tarballs dir?
if [ -r "${CT_TARBALLS_DIR}/${file}${ext}" ]; then
CT_DoLog DEBUG "Already have '${CT_TARBALLS_DIR}/${file}${ext}'"
if [ -r "${CT_TARBALLS_DIR}/${basename}${ext}" ]; then
CT_DoLog DEBUG "Already have '${CT_TARBALLS_DIR}/${basename}${ext}'"
return 0
fi
if [ -n "${CT_LOCAL_TARBALLS_DIR}" -a "${CT_FORCE_DOWNLOAD}" != "y" -a \
-r "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" ]; then
CT_DoLog DEBUG "Got '${file}' from local storage"
CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${file}${ext}" \
"${CT_TARBALLS_DIR}/${file}${ext}"
-r "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" ]; then
CT_DoLog DEBUG "Got '${basename}' from local storage"
CT_DoExecLog ALL ln -s "${CT_LOCAL_TARBALLS_DIR}/${basename}${ext}" \
"${CT_TARBALLS_DIR}/${basename}${ext}"
return 0
fi
done
# No, it does not... If not allowed to download from the Internet, don't.
if [ "${CT_FORBID_DOWNLOAD}" = "y" ]; then
CT_DoLog DEBUG "Not allowed to download from the Internet, aborting ${file} download"
CT_DoLog DEBUG "Not allowed to download from the Internet, aborting ${basename} download"
return 1
fi
# Try to retrieve the file
CT_DoLog EXTRA "Retrieving '${file}'"
CT_DoLog EXTRA "Retrieving '${basename}'"
# Add URLs on the LAN mirror
if [ "${CT_USE_MIRROR}" = "y" ]; then
@ -810,18 +931,31 @@ CT_GetFile() {
fi
if [ "${CT_FORCE_MIRROR}" != "y" ]; then
URLS+=( "${@}" )
URLS+=( ${mirrors} )
fi
# Scan all URLs in turn, and try to grab a tarball from there
# Do *not* try git trees (ext=/.git), this is handled in a specific
# wrapper, below
for ext in ${extensions}; do
# Try all urls in turn
for url in "${URLS[@]}"; do
[ -n "${url}" ] || continue
if CT_DoGetFile "${url}/${file}${ext}"; then
CT_SaveLocal "${CT_TARBALLS_DIR}/${file}${ext}"
if CT_DoGetFile "${url}/${basename}${ext}"; then
if [ -n "${digest}" ] && ! CT_DoVerifyDigest \
"${CT_TARBALLS_DIR}/${basename}${ext}" \
"${CT_LIB_DIR}/packages/${package}/${version}"; then
CT_DoLog ERROR "Digest verification failed; removing the download"
CT_DoExecLog ALL rm "${CT_TARBALLS_DIR}/${basename}${ext}"
return 1
fi
if [ -n "${signature_format}" ] && ! CT_DoVerifySignature \
"${CT_TARBALLS_DIR}/${basename}${ext}" \
"${url}/${basename}${ext}" \
"${signature_format}"; then
CT_DoLog ERROR "Signature verification failed; removing the download"
CT_DoExecLog ALL rm "${CT_TARBALLS_DIR}/${basename}${ext}"
return 1
fi
CT_SaveLocal "${CT_TARBALLS_DIR}/${basename}${ext}"
return 0
fi
done
@ -1742,7 +1876,7 @@ CT_PackageRun()
# Variables that are per-fork
for v in basename pkg_name version \
src_release mirrors archive_filename archive_dirname archive_formats \
src_release mirrors archive_filename archive_dirname archive_formats signature_format \
src_devel devel_vcs devel_url devel_branch devel_revision devel_subdir devel_bootstrap \
src_custom custom_location; do
eval "local ${v}=\${CT_${use}_${v^^}}"
@ -1780,7 +1914,11 @@ CT_DoFetch()
else
basename="${pkg_name}-${version}"
fi
if ! CT_GetFile "${pkg_name}" "${archive_filename}" "${archive_formats}" ${mirrors}; then
if ! CT_GetFile package="${pkg_name}" version="${version}" \
basename="${archive_filename}" extensions="${archive_formats}" \
digest="${CT_VERIFY_DOWNLOAD_DIGEST}" \
signature_format="${CT_VERIFY_DOWNLOAD_SIGNATURE:+${signature_format}}" \
mirrors="${mirrors}"; then
CT_Abort "${pkg_name}: download failed"
fi
@ -1811,7 +1949,8 @@ CT_DoFetch()
# attempt getting it from local storage or from the mirror if configured.
# Bzip2 offers a reasonable compromise between compression speed and size.
if [ "${unique_id}" != "to.be.determined" ] && \
CT_GetFile "${pkg_name}" "${basename}" '.tar.bz2'; then
CT_GetFile package="${pkg_name}" version="${version}" \
basename="${basename}" extensions='.tar.bz2'; then
return 0
fi
@ -1862,23 +2001,8 @@ CT_Extract()
CT_DoExecLog ALL mkdir -p "${dir}"
case "${file}" in
*.tar.xz)
xz -fdc "${file}" | CT_DoExecLog FILE tar x -v -f - -C "${dir}" ${components}
;;
*.tar.lzma)
xz -fdc "${file}" | CT_DoExecLog FILE tar x -v -f - -C "${dir}" ${components}
;;
*.tar.lz)
lzip -fdc "${file}" | CT_DoExecLog FILE tar x -v -f - -C "${dir}" ${components}
;;
*.tar.bz2)
bzip2 -dc "${file}" | CT_DoExecLog FILE tar x -v -f - -C "${dir}" ${components}
;;
*.tar.gz|*.tgz)
gzip -dc "${file}" | CT_DoExecLog FILE tar x -v -f - -C "${dir}" ${components}
;;
*.tar)
CT_DoExecLog FILE tar x -v -f "${file}" -C "${dir}" ${components}
*.tar.*|*.tar)
CT_ZCat "${file}" | CT_DoExecLog FILE tar x -v -f - -C "${dir}" ${components}
;;
*.zip)
CT_Pushd "${dir}"