summaryrefslogtreecommitdiffstats
path: root/usr.sbin/freebsd-update
diff options
context:
space:
mode:
authorcperciva <cperciva@FreeBSD.org>2008-08-02 00:09:41 +0000
committercperciva <cperciva@FreeBSD.org>2008-08-02 00:09:41 +0000
commitc911eed3dc2d5d9549b07884970368d5fcada0d8 (patch)
tree4ef003dcae9e1440e8345a59c95d94fd94e53509 /usr.sbin/freebsd-update
parent2c8efa0a9d8444fe1d62189ee201be5650cbbc7a (diff)
downloadFreeBSD-src-c911eed3dc2d5d9549b07884970368d5fcada0d8.zip
FreeBSD-src-c911eed3dc2d5d9549b07884970368d5fcada0d8.tar.gz
Add "IDS" command to freebsd-update. This was present in the original
version of freebsd-update, but I took it out when I rewrote everything and added FreeBSD Update to the base system because I didn't think it was useful. It turns out that quite a few people liked it and wanted it back. Requested by: Royce Williams + others MFC after: 2 weeks
Diffstat (limited to 'usr.sbin/freebsd-update')
-rw-r--r--usr.sbin/freebsd-update/freebsd-update.811
-rw-r--r--usr.sbin/freebsd-update/freebsd-update.sh255
2 files changed, 264 insertions, 2 deletions
diff --git a/usr.sbin/freebsd-update/freebsd-update.8 b/usr.sbin/freebsd-update/freebsd-update.8
index f50d36a..589886e 100644
--- a/usr.sbin/freebsd-update/freebsd-update.8
+++ b/usr.sbin/freebsd-update/freebsd-update.8
@@ -132,6 +132,9 @@ case there are any special steps needed for upgrading.
Install the most recently fetched updates or upgrade.
.It Cm rollback
Uninstall the most recently installed updates.
+.It Cm IDS
+Compare the system against a "known good" index of the
+installed release.
.El
.Sh TIPS
.Bl -bullet
@@ -144,6 +147,14 @@ to /etc/crontab will check for updates every night.
If your clock is set to UTC, please pick a random time
other than 3AM, to avoid overly imposing an uneven load
on the server(s) hosting the updates.
+.It
+In spite of its name,
+.Cm
+IDS should not be relied upon as an "Intrusion Detection
+System", since it if the system has been tampered with
+it cannot be trusted to operate correctly.
+If you intend to use this command for intrusion-detection
+purposes, make sure you boot from a secure disk (e.g., a CD).
.El
.Sh FILES
.Bl -tag -width "/etc/freebsd-update.conf"
diff --git a/usr.sbin/freebsd-update/freebsd-update.sh b/usr.sbin/freebsd-update/freebsd-update.sh
index 716051f..0f86a54 100644
--- a/usr.sbin/freebsd-update/freebsd-update.sh
+++ b/usr.sbin/freebsd-update/freebsd-update.sh
@@ -56,6 +56,7 @@ Commands:
upgrade -- Fetch upgrades to FreeBSD version specified via -r option
install -- Install downloaded updates or upgrades
rollback -- Uninstall most recently installed updates
+ IDS -- Compare the system against an index of "known good" files.
EOF
exit 0
}
@@ -86,7 +87,8 @@ EOF
CONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
- BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES"
+ BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
+ IDSIGNOREPATHS"
# Set all the configuration options to "".
nullconfig () {
@@ -222,6 +224,13 @@ config_IgnorePaths () {
done
}
+# Add to the list of paths which IDS should ignore.
+config_IDSIgnorePaths () {
+ for C in $@; do
+ IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
+ done
+}
+
# Add to the list of paths within which updates will be performed only if the
# file on disk has not been modified locally.
config_UpdateIfUnmodified () {
@@ -375,7 +384,7 @@ parse_cmdline () {
;;
# Commands
- cron | fetch | upgrade | install | rollback)
+ cron | fetch | upgrade | install | rollback | IDS)
COMMANDS="${COMMANDS} $1"
;;
@@ -692,6 +701,91 @@ rollback_check_params () {
fi
}
+# Perform sanity checks and set some final parameters
+# in preparation for comparing the system against the
+# published index. Figure out which index we should
+# compare against: If the user is running *-p[0-9]+,
+# strip off the last part; if the user is running
+# -SECURITY, call it -RELEASE. Chdir into the working
+# directory.
+IDS_check_params () {
+ export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
+
+ _SERVERNAME_z=\
+"SERVERNAME must be given via command line or configuration file."
+ _KEYPRINT_z="Key must be given via -k option or configuration file."
+ _KEYPRINT_bad="Invalid key fingerprint: "
+ _WORKDIR_bad="Directory does not exist or is not writable: "
+
+ if [ -z "${SERVERNAME}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_SERVERNAME_z}"
+ exit 1
+ fi
+ if [ -z "${KEYPRINT}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_KEYPRINT_z}"
+ exit 1
+ fi
+ if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
+ echo -n "`basename $0`: "
+ echo -n "${_KEYPRINT_bad}"
+ echo ${KEYPRINT}
+ exit 1
+ fi
+ if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ cd ${WORKDIR} || exit 1
+
+ # Generate release number. The s/SECURITY/RELEASE/ bit exists
+ # to provide an upgrade path for FreeBSD Update 1.x users, since
+ # the kernels provided by FreeBSD Update 1.x are always labelled
+ # as X.Y-SECURITY.
+ RELNUM=`uname -r |
+ sed -E 's,-p[0-9]+,,' |
+ sed -E 's,-SECURITY,-RELEASE,'`
+ ARCH=`uname -m`
+ FETCHDIR=${RELNUM}/${ARCH}
+ PATCHDIR=${RELNUM}/${ARCH}/bp
+
+ # Figure out what directory contains the running kernel
+ BOOTFILE=`sysctl -n kern.bootfile`
+ KERNELDIR=${BOOTFILE%/kernel}
+ if ! [ -d ${KERNELDIR} ]; then
+ echo "Cannot identify running kernel"
+ exit 1
+ fi
+
+ # Figure out what kernel configuration is running. We start with
+ # the output of `uname -i`, and then make the following adjustments:
+ # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config
+ # file says "ident SMP-GENERIC", I don't know...
+ # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
+ # _and_ `sysctl kern.version` contains a line which ends "/SMP", then
+ # we're running an SMP kernel. This mis-identification is a bug
+ # which was fixed in 6.2-STABLE.
+ KERNCONF=`uname -i`
+ if [ ${KERNCONF} = "SMP-GENERIC" ]; then
+ KERNCONF=SMP
+ fi
+ if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
+ if sysctl kern.version | grep -qE '/SMP$'; then
+ KERNCONF=SMP
+ fi
+ fi
+
+ # Define some paths
+ SHA256=/sbin/sha256
+ PHTTPGET=/usr/libexec/phttpget
+
+ # Set up variables relating to VERBOSELEVEL
+ fetch_setup_verboselevel
+}
+
#### Core functionality -- the actual work gets done here
# Use an SRV query to pick a server. If the SRV query doesn't provide
@@ -2705,6 +2799,157 @@ rollback_run () {
echo " done."
}
+# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
+IDS_compare () {
+ # Get all the non-matching lines.
+ sort -k 1,1 -t '|' < $1 > $1.sorted
+ comm -13 $1 $2 |
+ fgrep -v '|-||||||' |
+ sort -k 1,1 -t '|' |
+ join -t '|' $1.sorted - > INDEX-NOTMATCHING
+
+ # Ignore files which match IDSIGNOREPATHS.
+ for X in ${IDSIGNOREPATHS}; do
+ grep -E "^${X}" INDEX-NOTMATCHING
+ done |
+ sort -u |
+ comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
+ mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
+
+ # Go through the lines and print warnings.
+ while read LINE; do
+ FPATH=`echo "${LINE}" | cut -f 1 -d '|'`
+ TYPE=`echo "${LINE}" | cut -f 2 -d '|'`
+ OWNER=`echo "${LINE}" | cut -f 3 -d '|'`
+ GROUP=`echo "${LINE}" | cut -f 4 -d '|'`
+ PERM=`echo "${LINE}" | cut -f 5 -d '|'`
+ FLAGS=`echo "${LINE}" | cut -f 6 -d '|'`
+ HASH=`echo "${LINE}" | cut -f 7 -d '|'`
+ LINK=`echo "${LINE}" | cut -f 8 -d '|'`
+ P_TYPE=`echo "${LINE}" | cut -f 9 -d '|'`
+ P_OWNER=`echo "${LINE}" | cut -f 10 -d '|'`
+ P_GROUP=`echo "${LINE}" | cut -f 11 -d '|'`
+ P_PERM=`echo "${LINE}" | cut -f 12 -d '|'`
+ P_FLAGS=`echo "${LINE}" | cut -f 13 -d '|'`
+ P_HASH=`echo "${LINE}" | cut -f 14 -d '|'`
+ P_LINK=`echo "${LINE}" | cut -f 15 -d '|'`
+
+ # Warn about different object types.
+ if ! [ "${TYPE}" = "${P_TYPE}" ]; then
+ echo -n "${FPATH} is a "
+ case "${P_TYPE}" in
+ f) echo -n "regular file, "
+ ;;
+ d) echo -n "directory, "
+ ;;
+ L) echo -n "symlink, "
+ ;;
+ esac
+ echo -n "but should be a "
+ case "${TYPE}" in
+ f) echo -n "regular file."
+ ;;
+ d) echo -n "directory."
+ ;;
+ L) echo -n "symlink."
+ ;;
+ esac
+ echo
+
+ # Skip other tests, since they don't make sense if
+ # we're comparing different object types.
+ continue
+ fi
+
+ # Warn about different owners.
+ if ! [ "${OWNER}" = "${P_OWNER}" ]; then
+ echo -n "${FPATH} is owned by user id ${P_OWNER}, "
+ echo "but should be owned by user id ${OWNER}."
+ fi
+
+ # Warn about different groups.
+ if ! [ "${GROUP}" = "${P_GROUP}" ]; then
+ echo -n "${FPATH} is owned by group id ${P_GROUP}, "
+ echo "but should be owned by group id ${GROUP}."
+ fi
+
+ # Warn about different permissions. We do not warn about
+ # different permissions on symlinks, since some archivers
+ # don't extract symlink permissions correctly and they are
+ # ignored anyway.
+ if ! [ "${PERM}" = "${P_PERM}" ] &&
+ ! [ "${TYPE}" = "L" ]; then
+ echo -n "${FPATH} has ${P_PERM} permissions, "
+ echo "but should have ${PERM} permissions."
+ fi
+
+ # We don't warn about different file flags, since sysinstall
+ # doesn't seem to set these when it installs FreeBSD.
+
+ # Warn about different file hashes / symlink destinations.
+ if ! [ "${HASH}" = "${P_HASH}" ]; then
+ if [ "${TYPE}" = "L" ]; then
+ echo -n "${FPATH} is a symlink to ${P_HASH}, "
+ echo "but should be a symlink to ${HASH}."
+ fi
+ if [ "${TYPE}" = "f" ]; then
+ echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
+ echo "but should have SHA256 hash ${HASH}."
+ fi
+ fi
+
+ # We don't warn about different hard links, since some
+ # some archivers break hard links, and as long as the
+ # underlying data is correct they really don't matter.
+ done < INDEX-NOTMATCHING
+
+ # Clean up
+ rm $1 $1.sorted $2 INDEX-NOTMATCHING
+}
+
+# Do the work involved in comparing the system to a "known good" index
+IDS_run () {
+ workdir_init || return 1
+
+ # Prepare the mirror list.
+ fetch_pick_server_init && fetch_pick_server
+
+ # Try to fetch the public key until we run out of servers.
+ while ! fetch_key; do
+ fetch_pick_server || return 1
+ done
+
+ # Try to fetch the metadata index signature ("tag") until we run
+ # out of available servers; and sanity check the downloaded tag.
+ while ! fetch_tag; do
+ fetch_pick_server || return 1
+ done
+ fetch_tagsanity || return 1
+
+ # Fetch INDEX-OLD and INDEX-ALL.
+ fetch_metadata INDEX-OLD INDEX-ALL || return 1
+
+ # Generate filtered INDEX-OLD and INDEX-ALL files containing only
+ # the components we want and without anything marked as "Ignore".
+ fetch_filter_metadata INDEX-OLD || return 1
+ fetch_filter_metadata INDEX-ALL || return 1
+
+ # Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
+ sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
+ mv INDEX-ALL.tmp INDEX-ALL
+ rm INDEX-OLD
+
+ # Translate /boot/${KERNCONF} to ${KERNELDIR}
+ fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
+
+ # Inspect the system and generate an INDEX-PRESENT file.
+ fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
+
+ # Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
+ # differences.
+ IDS_compare INDEX-ALL INDEX-PRESENT
+}
+
#### Main functions -- call parameter-handling and core functions
# Using the command line, configuration file, and defaults,
@@ -2765,6 +3010,12 @@ cmd_rollback () {
rollback_run || exit 1
}
+# Compare system against a "known good" index.
+cmd_IDS () {
+ IDS_check_params
+ IDS_run || exit 1
+}
+
#### Entry point
# Make sure we find utilities from the base system
OpenPOWER on IntegriCloud