[CentOS-devel] Re: rpm verification tool
Jan Iven
jan.iven at cern.ch
Fri Nov 9 17:38:33 UTC 2007
> Sure ... it is here:
>
> http://mirror.centos.org/centos/4/build/distro/tmverifyrpms
>
> You can use it to compare RPMS of the same name in a CentOS and RHEL
> directory that reside in the same directory as the script.
Thanks. The idea sounds very useful - I've started on modifying your
script (attached) to cope with other directory structures and take
signatures etc into account (should still largely behave as before,
unless you give it arguments..).
I am not sure whether a "common" version for CentOS/SL/SLC/.. makes
sense (the Changelog seem to document several
generalization+simplification/specialization cycles already..), but if
Jeff now adds new checks to your version, I'd be happy to benefit from
them as well.
Best regards
jan
-------------- next part --------------
#!/bin/bash
# tmverifyrpms.sh
# http://mirror.centos.org/centos/4/build/distro/tmverifyrpms
# Check a set of rpms against a reference.
# Ideas from David Cox's perl script ported to bash
# creates tmverifyrpms.log in repository where it is run in the form of:
# rpmname-cmp OK|OK-PERFECT|FAIL ref:refsize +/-:sizediffabs %:sizediff%
# Where
# - refsize is the rpm -q --qf %{SIZE} of the ref rpm
# - sizediff% is the % difference between cmp and ref
# - sizediffabs is the difference in bytes
# If there are library, or file name/perm differences, create
# reports/rpm.out
#
# Version 4.1.5-CentOS
# 2007-02-27 Johnny Hughes <johnny at centos.org>
# - Modified so that it will match some of the standard file
# extensions we use (.centos, .c4. .el4.centos) with their
# reference counterparts w/o those extensions
# 2005-07-13 Johnny Hughes <johnny at centos.org>
# - Modified David's great script to run standalone
# for using in CentOS compares.
# - Removed many of the features (run in background,
# srpms maps, etc.) which are not needed in this
# implementation of the script
#
# You can get all of David's tmtools as a group
# from the /tmtools directory of any taolinux
# mirror. See http://www.taolinux.com for details
#
# Thanks to David for a great script :)
#
# Version 4.1.4
# 2004-12-23 David L. Parsley <parsley at linuxjedi.org>
# - Sort rpm --requires output
# 2004-12-21 parsley at linuxjedi.org
# - Findrepo & cd there first (to run in subdirs)
# - Put error checking before the fork-to-background
# 2004-12-03 parsley at linuxjedi.org
# - Discard rpm errors
# - Fix sign error in SZDIFF
# 2004-11-30 parsley at linuxjedi.org
# - Strip (xxx) from end (ld-linux.so mainly)
# - Add checking of rpm --requires
# 2004-09-30 David L. Parsley <parsley at linuxjedi.org>
# - use mktemp -u, or files will be mode 600
# 2004-05-16 David L. Parsley <parsley at linuxjedi.org>
# - Add user & group to fingerprinting
# - Strip right-hand-side of library linkages (after =>)
# - dump stderr by default (can override locally)
# 2004-05-01 David L. Parsley <parsley at linuxjedi.org>j
# - use rpm --dump for files, can run unpriv
#set -x
#
# This has been modified to become a standalone compare tool
# by Johnny Hughes for CentOS compares on 7/13/2005
#
# more modifications:
# take some input arguments to cope with different layouts, allow for several directories to compare with
# remove dependency on current directory, quote everything to cope with whitespace in path
# use $TMP iff set
# make "workdir" temporary.
# extend patterns for modified RPM names to include SL
STARTDIR=`pwd`
#This is where the reference files are
REFDIRS=$STARTDIR/RHEL
#This is the directory with files to be compared
CMPDIR=$STARTDIR/CentOS
#Reporting goes here
RPTDIR=$STARTDIR/reports
# optional: want signed RPMs
RPMNEEDSIGN=
# optional: insist on this signing key
RPMGPGKEY=
WORKDIR=`mktemp -t -d rpmverify-work.XXXXX`
# indicator that something bad was found.
GLOBALFAIL=0
help() {
cat <<EOFhelp
Usage: tmverifyrpms [-h] [-s] [-g keyid] [-r refdir,refdir] [-c cmpdir] [-l logdir]
compare RPMs in cmpdir (default:$CMPDIR) to those in refdirs (default:$REFDIRS),
list differences into logdir (default:$RPTDIR).
-h: help
-s: want signed RPMs
-g: like -s, but accept only this keyid
EOFhelp
exit 0
}
while getopts "hsvg:r:c:l:w:" flag
do
[ "$flag" == "h" ] && help
[ "$flag" == "s" ] && RPMNEEDSIGN=1
[ "$flag" == "g" ] && RPMGPGKEY="${OPTARG##0x%}"
[ "$flag" == "r" ] && REFDIRS="${OPTARG//,/ /}"
[ "$flag" == "c" ] && CMPDIR="${OPTARG%%/%}"
[ "$flag" == "l" ] && RPTDIR="${OPTARG%%/%}"
[ "$flag" == "w" ] && WORKDIR="${OPTARG%%/%}"
done
[ -n "$RPMGPGKEY" ] && RPMNEEDSIGN=1
if [ -d "$WORKDIR" ]
then
rm -rf "$WORKDIR"
fi
mkdir -p "$RPTDIR" "$WORKDIR"
exec 6>&1
exec 1>"$RPTDIR/tmverifyrpms.log"
rpmarch(){
STRIPPED=${1%.rpm}
echo ${STRIPPED##*.}
}
genfilelist(){
rpm -qp --dump "$1" 2>/dev/null | LC_ALL=C sort | while read DUMPLINE
do
DUMPLINE=${DUMPLINE/ [01] [01] /TMLINESEP}
FSYMLINK=${DUMPLINE#*TMLINESEP* }
DUMPLINE=${DUMPLINE%TMLINESEP*}
FILENAME="${DUMPLINE% * * * * * *}"
REST=${DUMPLINE#$FILENAME }
set $REST
echo "$FILENAME mode:$4 owner:$5 group:$6 linkto:$FSYMLINK"
done
}
comprpms(){
CMPRPM="$1"
REFRPM="$2"
RPM=`basename "$CMPRPM"`
PKGOK="OK"
CMPRPMDIR=`mktemp -t -d cmprpm.XXXXXX`
REFRPMDIR=`mktemp -t -d refrpm.XXXXXX`
rm -f "$RPTDIR/$RPM.out"
rpm2cpio "$CMPRPM" | ( cd "$CMPRPMDIR"; cpio -id --no-absolute-filenames --quiet )
rpm2cpio "$REFRPM" | ( cd "$REFRPMDIR"; cpio -id --no-absolute-filenames --quiet )
cd "$CMPRPMDIR"
find . ! -type d | while read FILENAME
do
if [ -x "$FILENAME" -a -f "$FILENAME" ]
then
echo $FILENAME >> $WORKDIR/CMPDIR-libs
ldd "$FILENAME" 2>&1 | LC_ALL=C sort >> "$WORKDIR/CMPDIR-libs"
fi
done
cd "$REFRPMDIR"
find . ! -type d | while read FILENAME
do
if [ -x "$FILENAME" -a -f "$FILENAME" ]
then
echo "$FILENAME" >> "$WORKDIR/REFDIR-libs"
ldd "$FILENAME" 2>&1 | LC_ALL=C sort >> "$WORKDIR/REFDIR-libs"
fi
done
genfilelist "$CMPRPM" > "$WORKDIR/CMPDIR-list"
genfilelist "$REFRPM" > "$WORKDIR/REFDIR-list"
diff -u "$WORKDIR/CMPDIR-list" "$WORKDIR/REFDIR-list" > "$WORKDIR/rpmdiff"
if [ $? -eq 1 ]
then
PKGOK="FAIL"
echo "Differing file fingerprints:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/rpmdiff" >> "$RPTDIR/$RPM.out"
fi
rpm -qp --requires "$CMPRPM" 2>/dev/null | LC_ALL=C sort >"$WORKDIR/CMPDIR-req"
rpm -qp --requires "$REFRPM" 2>/dev/null | LC_ALL=C sort >"$WORKDIR/REFDIR-req"
diff -u "$WORKDIR/CMPDIR-req" "$WORKDIR/REFDIR-req" > "$WORKDIR/reqdiff"
if [ $? -eq 1 ]
then
PKGOK="FAIL"
echo "Differing package requirements:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/reqdiff" >> "$RPTDIR/$RPM.out"
fi
if [ -e "$WORKDIR/REFDIR-libs" ]
then
# Only want what it's looking for, not what it actually gets
perl -pi -e 's/ =>.*//' "$WORKDIR/CMPDIR-libs"
perl -pi -e 's/ =>.*//' "$WORKDIR/REFDIR-libs"
perl -pi -e 's/ \(.*//' "$WORKDIR/CMPDIR-libs"
perl -pi -e 's/ \(.*//' "$WORKDIR/REFDIR-libs"
diff -u "$WORKDIR/CMPDIR-libs" "$WORKDIR/REFDIR-libs" > "$WORKDIR/libdiff"
if [ $? -eq 1 ]
then
PKGOK="FAIL"
echo "Differing libraries:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/libdiff" >> "$RPTDIR/$RPM.out"
echo "ldd for CMPDIR-rpm:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/CMPDIR-libs" >> "$RPTDIR/$RPM.out"
echo "ldd for REFDIR-rpm:">> "$RPTDIR/$RPM.out"
cat "$WORKDIR/REFDIR-libs" >> "$RPTDIR/$RPM.out"
fi
fi
if [ -e "$WORKDIR/CMPDIR-libs" ]
then
grep -q 'not found' "$WORKDIR/CMPDIR-libs"
if [ $? -eq 0 ]
then
PKGOK="FAIL"
echo "Missing libraries:" \
>> "$RPTDIR/$RPM.out"
cat "$WORKDIR/CMPDIR-libs" >> "$RPTDIR/$RPM.out"
fi
fi
rm -f $WORKDIR/libdiff $WORKDIR/rpmdiff CMPDIR-libs REFDIR-libs
# MORE TESTS HERE
CMPSIZE=`rpm -qp --qf %{SIZE} $CMPRPM 2>/dev/null`
REFSIZE=`rpm -qp --qf %{SIZE} $REFRPM 2>/dev/null`
SZDIFF=$(($CMPSIZE - $REFSIZE))
ABSDIFF=$((SZDIFF<0?-$SZDIFF:$SZDIFF))
if [ $REFSIZE -ne 0 ]
then
PCTDIFF=$(($ABSDIFF * 100 / $REFSIZE))
else
PCTDIFF=-0-
fi
if [ $PKGOK = "OK" -a $SZDIFF -eq 0 ]
then
PKGOK="OK-PERFECT"
fi
rm -rf $CMPRPMDIR $REFRPMDIR
[ "$PKGOK" = "FAIL" ] && GLOBALFAIL=$(( GLOBALFAIL++ ))
RESULTS="$RPM $PKGOK ref:$REFSIZE +/-:$SZDIFF %:$PCTDIFF"
}
# Get list of rpms to compare
RPMLIST=`echo ${CMPDIR}/*.rpm`
for CMPRPM in $RPMLIST
do
CMPRPM="${CMPRPM##${CMPDIR}/}"
echo -n "Verifying $CMPRPM ..."
# tests done directly on the RPM, i.e. not comparing with something
# readable?
if [ ! -r "${CMPDIR}/$CMPRPM" ]
then
echo ""
echo "$CMPRPM FAIL - unreadable"
echo "-------------------------"
GLOBALFAIL=$(($GLOBALFAIL++))
continue
fi
# corrupt, signed etc - try to run rpmk only once...
verify=`rpm -Kv "${CMPDIR}/$CMPRPM" 2>&1`
if ! echo "$verify" | grep -q "Header SHA1 digest: OK"
then
echo ""
echo "$CMPRPM FAIL - corrupted header or not an RPM"
echo "corrupted header or not an RPM:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
echo "-------------------------"
GLOBALFAIL=$((GLOBALFAIL++))
continue
fi
echo -n "."
if ! echo "$verify" | grep -q "MD5 digest: OK"
then
echo ""
echo "$CMPRPM FAIL - corrupted payload"
echo "corrupted payload:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
echo "-------------------------"
GLOBALFAIL=$((GLOBALFAIL++))
continue
fi
echo -n "."
# do we insist on signed RPMs?
if [ -n "$RPMNEEDSIGN" ]
then
if ! echo "$verify" | grep -q "Header V3 DSA signature" ||\
! echo "$verify" | grep -q "V3 DSA signature"
then
echo ""
echo "$CMPRPM FAIL - not signed"
echo "not signed:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
echo "-------------------------"
GLOBALFAIL=$((GLOBALFAIL++))
continue
fi
echo -n "."
if ! echo "$verify" | grep -q "V3 DSA signature: OK" ||\
! echo "$verify" | grep -q "Header V3 DSA signature: OK"
then
echo ""
echo "$CMPRPM FAIL - signed with unknown key or bad signature"
echo "signed with unknown key or bad signature:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
echo "-------------------------"
GLOBALFAIL=$((GLOBALFAIL++))
continue
fi
echo -n "."
# do we insist on a specific key?
if [ -n "$RPMGPGKEY" ]
then
if ! echo "$verify" | grep -q "V3 DSA signature: OK, key ID $RPMGPGKEY" ||\
! echo "$verify" | grep -q "Header V3 DSA signature: OK, key ID $RPMGPGKEY"
then
echo ""
echo "$CMPRPM FAIL - not signed with distro key $RPMGPGKEY"
echo "not signed with distro key $RPMGPGKEY:" >> "$RPTDIR/$CMPRPM.out"
echo "$verify" >> "$RPTDIR/$CMPRPM.out"
echo "-------------------------"
GLOBALFAIL=$((GLOBALFAIL++))
continue
fi
echo -n "."
fi
fi
# comparisons to some upstream RPM
for REFDIR in $REFDIRS
do
# remove trailing slashes
REFDIR="${REFDIR%%/%}"
# prefer to look for exactly-named RPMs
if [ -e "$REFDIR/$CMPRPM" ]; then
comprpms "$REFDIR/$CMPRPM" "$CMPDIR/$CMPRPM"
echo ""
echo $RESULTS
echo "-------------------------"
continue 2
fi
# try with the original-non-distro name
no_changed="$CMPRPM"
for pat in .el4.centos .el5.centos .centos4 .centos .c4 .SL4 .cern .slc4 .slc; do
# should be able to use {,pattern,pattern,..} but doesn't work with bash-2
REFRPM="${no_changed//${pat}/}"
done
if [ -e "$REFDIR/$no_changed" ]; then
echo -n "(distro tag)... "
comprpms "$REFDIR/CMPRPM" "$CMPDIR/$REFRPM"
echo ""
echo $RESULTS
echo "-------------------------"
continue 2
fi
done
echo "no match found for $CMPRPM"
echo "-------------------------"
done
echo "Finished with, $GLOBALFAIL errors"
exec 1>&6
if [ "$GLOBALFAIL" -gt 0 ]
echo "There were $GLOBALFAIL errors - see $RPTDIR/tmverifyrpms.log" >2
exit 1
fi
More information about the CentOS-devel
mailing list