384 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			384 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/sh
 | |
| #
 | |
| # Copyright (c) 2013 - 2017 M:tier Ltd.
 | |
| #
 | |
| # Permission to use, copy, modify, and distribute this software for any
 | |
| # purpose with or without fee is hereby granted, provided that the above
 | |
| # copyright notice and this permission notice appear in all copies.
 | |
| #
 | |
| # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | |
| # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | |
| # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | |
| # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | |
| # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | |
| # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | |
| # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | |
| #
 | |
| # Author: Antoine Jacoutot <antoine@mtier.org>
 | |
| # Maintainer: M:Tier Ltd. <info@mtier.org>
 | |
| 
 | |
| # ChangeLog
 | |
| # https://gitlab.com/mtier/openup/commits/master
 | |
| 
 | |
| ########################################################################
 | |
| ### DO NOT EDIT THIS FILE!!!                                         ###
 | |
| ### User defined variables: overrides are read from /etc/openup.conf ###
 | |
| ########################################################################
 | |
| 
 | |
| # URL to the latest openup version
 | |
| OPENUP_URL="https://stable.mtier.org/openup"
 | |
| 
 | |
| # signify(1) public key
 | |
| PKG_PUBKEY_URL="https://stable.mtier.org/mtier-$(uname -r | tr -d '.')-pkg.pub"
 | |
| 
 | |
| # PKG_PATH for currently running OpenBSD release
 | |
| PKG_PATH_MAIN="https://ftp.fr.openbsd.org/pub/OpenBSD/$(uname -r)/packages/$(arch -s)"
 | |
| 
 | |
| # ERRATA_PATH
 | |
| ERRATA_PATH="https://ftp.fr.openbsd.org/pub/OpenBSD/patches/$(uname -r)/common"
 | |
| 
 | |
| # PKG_PATH for the corresponding release stable service
 | |
| PKG_PATH_UPDATE="https://stable.mtier.org/updates/$(uname -r)/$(arch -s)"
 | |
| 
 | |
| # PKG_PATH addition for the corresponding LTS release stable service
 | |
| #PKG_PATH_UPDATE_LTS="https://user%domain.tld:password@stable.mtier.org/updates-lts/$(uname -r)/$(arch -s)"
 | |
| 
 | |
| # URL to the latest vuxml (vulnerabilities database)
 | |
| VUXML_URL="https://stable.mtier.org/vuxml/$(uname -r | tr -d '.').xml"
 | |
| 
 | |
| # fetch command (must behave like "ftp -o"); e.g. "wget -qO"
 | |
| FETCH="ftp -MVo"
 | |
| 
 | |
| # exclusion list: pkg names without version/flavor, separated by space
 | |
| EXCLUDE_PKG=""
 | |
| 
 | |
| ########################################################################
 | |
| ### End of user defined variables                                    ###
 | |
| ########################################################################
 | |
| 
 | |
| usage() {
 | |
| 	echo
 | |
| 	echo "Usage: ${0##*/} [-K][-Se|c]" >&2
 | |
| 	echo
 | |
| 	echo "Options:"
 | |
| 	echo "  -K    do not check for kernel binpatches (when running non GENERIC)"
 | |
| 	echo "  -N    do not check for syspatches"
 | |
| 	echo "  -S    ignore binpatch/package signatures"
 | |
| 	echo "  -c    check/cron mode, report only (cannot be used with -S)"
 | |
| 	echo "  -e    echo the PKG_PATH that ${0##*/} would use"
 | |
| 	echo
 | |
| 	exit 1
 | |
| }
 | |
| 
 | |
| pr_err() {
 | |
| 	echo "!!! ${@}"
 | |
| }
 | |
| 
 | |
| bye_bye() {
 | |
| 	rm -rf ${_TMPDIR} ${_PID}
 | |
| 	exit 1
 | |
| }
 | |
| 
 | |
| pr() {
 | |
| 	if [ -z "${checkrun}" ]; then
 | |
| 		echo "===> ${@}"
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| get_key() {
 | |
| 	[ -r /etc/signify/mtier-${_REL_INT}-pkg.pub ] && return
 | |
| 
 | |
| 	pr "Downloading and installing public key"
 | |
| 	${FETCH} ${_TMPKEY} ${PKG_PUBKEY_URL} || bye_bye
 | |
| 	install -m0644 ${_TMPKEY} /etc/signify/mtier-${_REL_INT}-pkg.pub || bye_bye
 | |
| }
 | |
| 
 | |
| show_env() {
 | |
| 	echo PKG_PATH=${PKG_PATH_UPDATE_LTS}:${PKG_PATH_UPDATE}:${PKG_PATH_MAIN}
 | |
| 	exit 0
 | |
| }
 | |
| 
 | |
| check_openupd() {
 | |
| 	local _U
 | |
| 
 | |
| 	pr "Checking for openup update"
 | |
| 	_U="${FETCH} - ${OPENUP_URL} | awk -F '=' '/^_OPENUP_VERSION/ { print \$2 }'"
 | |
| 	_U=$(eval $_U)
 | |
| 	if [ -z "${_U}" ]; then
 | |
| 		pr_err "Cannot retrieve ${OPENUP_URL}"
 | |
| 		pr_err "Please verify your Internet connection, proxy settings and firewall."
 | |
| 		bye_bye
 | |
| 	fi
 | |
| 
 | |
| 	if [ "${_OPENUP_VERSION}" -lt "${_U}" ]; then
 | |
| 		pr_err "New openup release (version ${_U}) available; please update with:"
 | |
| 		pr_err "${FETCH} $(readlink -f $0) ${OPENUP_URL}"
 | |
| 		bye_bye
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| # check that we have no installed binpatches from a previous release and
 | |
| # if so remove the entries manually (we don't want pkg_delete to
 | |
| # modify nor error out on installed files from newer release/binpatch)
 | |
| rm_old_bp() {
 | |
| 	local _bp
 | |
| 	local _BPDB=$(ls -d /var/db/binpatch/{binpatch-,}[0-9]* 2>/dev/null |grep -v ${_REL})
 | |
| 	local _BPPKG=$(ls -d /var/db/pkg/binpatch* 2>/dev/null |grep -v binpatch${_REL_INT})
 | |
| 	if [ -n "${_BPPKG}" -o -n "${_BPDB}" ]; then
 | |
| 		pr "Removing old release binpatch entries"
 | |
| 	fi
 | |
| 	for _bp in ${_BPPKG} ${_BPDB}; do
 | |
| 		rm -rf ${_bp}
 | |
| 	done
 | |
| }
 | |
| 
 | |
| update_syspatches() {
 | |
| 	pr "Installing/updating syspatches"
 | |
| 	syspatch
 | |
| 	if [ $? -gt 0 ]; then
 | |
| 		pr_err "syspatch failed; please run syspatch manually"
 | |
| 		bye_bye
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| update_binpatches() {
 | |
| 	local _BP _b _e
 | |
| 
 | |
| 	# binpatches can only be found in PKG_PATH_UPDATE{,_LTS) and we
 | |
| 	# want to make sure we search in all paths and don't stop at the
 | |
| 	# first match we find
 | |
| 	for i in ${PKG_PATH_UPDATE_LTS} ${PKG_PATH_UPDATE}; do
 | |
| 		_BP="$(pkg_info -Q binpatch${_REL_INT}-${_ARCH} | sed 's/.[^-]*$//' | sort -u)${_BP:+ ${_BP}}"
 | |
| 	done
 | |
| 
 | |
| 	if [ -n "${_BP}" ]; then
 | |
| 		for _e in ${EXCLUDE_PKG}; do
 | |
| 			set -A _BP -- ${_BP}
 | |
| 			_BP="$(for _b in ${_BP[@]}; do echo ${_b} | grep -v "^${_e}$"; done)"
 | |
| 		done
 | |
| 		_BP=$(echo "${_BP}" | tr '\n' ' ')
 | |
| 
 | |
| 		pr "Installing/updating binpatch(es)"
 | |
| 		pkg_add ${pkgopt} ${_BP} || bye_bye
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| update_pkg() {
 | |
| 	local _PKG _e _p
 | |
| 
 | |
| 	_PKG=$(pkg_info -q | grep -v binpatch${_REL_INT}-${_ARCH})
 | |
| 
 | |
| 	if [ -n "${_PKG}" ]; then
 | |
| 		for _e in ${EXCLUDE_PKG}; do
 | |
| 			set -A _PKG -- ${_PKG}
 | |
| 			_PKG="$(for _p in ${_PKG[@]}; do echo ${_p} | grep -v "^${_e}-.*"; done)"
 | |
| 		done
 | |
| 		_PKG=$(echo "${_PKG}" | tr '\n' ' ')
 | |
| 
 | |
| 		pr "Updating package(s)"
 | |
| 		pkg_add -quz ${pkgopt} ${_PKG} || bye_bye
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| # this only outputs the missing syspatches on the system
 | |
| check_syspatches() {
 | |
| 	_SYSPATCHES=$(syspatch -c)
 | |
| 	for _p in ${_SYSPATCHES}; do
 | |
| 		${FETCH} ${_TMPERRATA} ${ERRATA_PATH}/${_p}.patch.sig
 | |
| 		[ -e ${_TMPERRATA} ] || continue
 | |
| 		echo "--- ${_p} ---"
 | |
| 		echo -n "Available errata: "
 | |
| 		awk '/OpenBSD/{f=1;next} /Apply/{f=0;next} f' ${_TMPERRATA} | grep -ve '^$'
 | |
| 	done
 | |
| }
 | |
| 
 | |
| # this only outputs the most recent vulnerability for each matching pkg
 | |
| check_vuxml() {
 | |
| 	local _BP_MATCH _OUTDATED _PKG_MATCH _b _e _p
 | |
| 
 | |
| 	${FETCH} ${_TMPVUXML} ${VUXML_URL} || bye_bye
 | |
| 	perl -pi -e 's,\$ARCH,'"${_ARCH}"',g' ${_TMPVUXML}
 | |
| 
 | |
| 	if [ "${_REL_INT}" -lt 61 ]; then
 | |
| 		_BP_MATCH="$(grep binpatch ${_TMPVUXML} | sed -e 's,<name>,,g;s,</name>,,g;s,\$ARCH,'"${_ARCH}"',g' | sort -u)"
 | |
| 		for _e in ${EXCLUDE_PKG}; do
 | |
| 			set -A _BP_MATCH -- ${_BP_MATCH}
 | |
| 			_BP_MATCH="$(for _b in ${_BP_MATCH[@]}; do echo ${_b} | grep -v "^${_e}$"; done)"
 | |
| 		done
 | |
| 	fi
 | |
| 
 | |
| 	_PKG_MATCH=$(pkg_info -q | grep -v binpatch${_REL_INT}-${_ARCH})
 | |
| 
 | |
| 	for _e in ${EXCLUDE_PKG}; do
 | |
| 		set -A _PKG_MATCH -- ${_PKG_MATCH}
 | |
| 		_PKG_MATCH="$(for _p in ${_PKG_MATCH[@]}; do echo ${_p} | grep -v "^${_e}-.*"; done)"
 | |
| 	done
 | |
| 
 | |
| 	if [ "${_REL_INT}" -lt 61 ]; then
 | |
| 		# set to "quirks" if empty to prevent running pkg_add against an empty pkg list
 | |
| 		_OUTDATED=$((pkg_add -Iqn -Dnosig -Dunsigned ${_BP_MATCH:=quirks}; pkg_add -Iqnuz -Dnosig -Dunsigned ${_PKG_MATCH:=quirks}) 2>&1 | \
 | |
| 		    grep '^NOT CHECKING DIGITAL SIGNATURE FOR ' | \
 | |
| 		    sed -e 's,^NOT CHECKING DIGITAL SIGNATURE FOR ,,g' | \
 | |
| 		    grep -v '^quirks-' | \
 | |
| 		    perl -ne '/^(.*)-(\d[^-]*)[-]?(\w*)(.*)$/ && print "$1\n"' | \
 | |
| 		    sort -u)
 | |
| 	else
 | |
| 		# set to "quirks" if empty to prevent running pkg_add against an empty pkg list
 | |
| 		_OUTDATED=$((pkg_add -Inuvz -Dnosig -Dunsigned ${_PKG_MATCH:=quirks}) 2>&1 | \
 | |
| 		    grep '^Adding.*(pretending)' | sort -u | sed -e 's,Adding ,,;s,(pretending),,;s,->.*,,' | \
 | |
| 		    grep -v '^quirks-' | \
 | |
| 		    perl -ne '/^(.*)-(\d[^-]*)[-]?(\w*)(.*)$/ && print "$1\n"' | \
 | |
| 		    sort -u)
 | |
| 	fi
 | |
| 
 | |
| 	for p in ${_OUTDATED}
 | |
| 	do
 | |
| 		echo "--- ${p} ---\n"
 | |
| 		echo "Available update(s): "
 | |
| 		# XXX how do we print only the 1st matching range in awk?
 | |
| 		awk "/<name>${p}<\/name>/,/<\/vuln>/" ${_TMPVUXML} | \
 | |
| 			sed '/<\/vuln>/,$d' | \
 | |
| 			sed -n -e 's/.*<range><lt>\(.*\)<\/lt><\/range>.*/\1/p' \
 | |
| 			       -e 's/.*<p>\(.*\)<\/p>.*/\1/p' | uniq | \
 | |
| 			while read l; do echo -n "${l} "; done
 | |
| 			echo "\n"
 | |
| 	done | fmt | sed '/^$/d'
 | |
| }
 | |
| 
 | |
| do_i_need_to_reboot() {
 | |
| 	# XXX hardcoded PKG_DBDIR
 | |
| 	local osversion=$(sysctl -n kern.osversion)
 | |
| 	local kern_version=$(sysctl -n kern.version | head -1)
 | |
| 	local booted_version=$(what -s /bsd | tail -1 | tr -d '\t')
 | |
| 	local booted_time=$(sysctl -n kern.boottime)
 | |
| 	local kern_time=$(stat -qf "%Um" /bsd)
 | |
| 	local warn=0
 | |
| 
 | |
| 	# Check /bsd.booted first if available due to kernel relinking
 | |
| 	if [ -e /bsd.booted ]; then
 | |
| 		[ X"${kern_version}" != X"${booted_version}" ] && warn=1
 | |
| 	else
 | |
| 		[ "${booted_time}" -lt "${kern_time}" ] && warn=1
 | |
| 	fi
 | |
| 
 | |
| 	if [ ${warn} -gt 0 ]; then
 | |
| 		pr_err
 | |
| 		pr_err "System must be rebooted after the last kernel update"
 | |
| 		pr_err
 | |
| 	fi
 | |
| }
 | |
| 
 | |
| patch_syspatch() {
 | |
| 	pr_err
 | |
| 	pr_err "Patching syspatch(8) to use syspatches signed by MTier."
 | |
| 	pr_err
 | |
| 	cp /usr/sbin/syspatch /usr/sbin/syspatch.pre-mtier
 | |
| 	sed -i \
 | |
| 		"s,openbsd-\${_OSrev}-syspatch.pub,mtier-\${_OSrev}-pkg.pub,g; \
 | |
| 		s,/etc/installurl,/etc/installurl-mtier,g" \
 | |
| 		/usr/sbin/syspatch
 | |
| 	[ -e /etc/installurl-mtier ] || echo https://stable.mtier.org > /etc/installurl-mtier
 | |
| }
 | |
| 
 | |
| trap "bye_bye" 1 2 3 13 15
 | |
| 
 | |
| if [ -f /etc/openup.conf ]; then
 | |
| 	if [ $(stat -f "%SMp%SLp" /etc/openup.conf) != "------" ]; then
 | |
| 		pr_err "Unsecure permissions on /etc/openup.conf; please run:"
 | |
| 		pr_err "chmod 0600 /etc/openup.conf"
 | |
| 		exit 1
 | |
| 	fi
 | |
| 	. /etc/openup.conf
 | |
| fi
 | |
| 
 | |
| # regex taken from fw_update(1)
 | |
| set -A _REL -- $(sysctl -n kern.version | sed 's/^OpenBSD \([0-9]\.[0-9]\)\([^ ]*\).*/\1 \2/;q')
 | |
| _REL_INT="$(echo ${_REL[0]} | tr -d '.')"
 | |
| _OPENUP_MINREL=60
 | |
| _OPENUP_VERSION=33
 | |
| if [ -n "${_REL[1]}" -a "${_REL[1]}" != "-stable" ]; then _badrel=1; fi
 | |
| if [ "${_REL_INT}" -lt "${_OPENUP_MINREL}" ]; then _badrel=1; fi
 | |
| if [ -n "${_badrel}" ]; then
 | |
| 	pr_err "${_REL[0]}${_REL[1]} is not a supported release"
 | |
| 	exit 1
 | |
| fi
 | |
| 
 | |
| while getopts 'KNSce' arg; do
 | |
| 	case ${arg} in
 | |
| 	K)	if [ "${_REL_INT}" -lt 61 ]; then nokrn=1; fi ;;
 | |
| 	N)	nosyspatch=1 ;;
 | |
| 	S)	nosig=1; pkgopt="${pkgopt} -Dnosig -Dunsigned" ;;
 | |
| 	c)	checkrun=1 ;;
 | |
| 	e)	showenv=1 ;;
 | |
| 	*)	usage ;;
 | |
| 	esac
 | |
| done
 | |
| [ $# = $(($OPTIND-1)) ] || usage
 | |
| 
 | |
| [ -n "${showenv}" ] && show_env
 | |
| 
 | |
| if [ "$(id -u)" -ne 0 ]; then
 | |
| 	pr_err "Need root privileges to run this script"
 | |
| 	usage
 | |
| fi
 | |
| 
 | |
| [ -n "${checkrun}" -a -n "${nosig}" -a -n "${showenv}" ] && usage
 | |
| 
 | |
| _ARCH=$(arch -s)
 | |
| _PID="/var/run/${0##*/}.pid"
 | |
| _TMP="${TMPDIR:=/tmp}"
 | |
| _TMPDIR=$(mktemp -dp ${_TMP} .openup-XXXXXXXXXX) || exit 1
 | |
| _TMPKEY="${_TMPDIR}/key"
 | |
| _TMPVUXML="${_TMPDIR}/vuxml"
 | |
| _TMPERRATA="${_TMPDIR}/errata"
 | |
| 
 | |
| export PKG_PATH=${PKG_PATH_UPDATE_LTS}:${PKG_PATH_UPDATE}:${PKG_PATH_MAIN}
 | |
| 
 | |
| if [ -f ${_PID} ]; then
 | |
| 	pr_err "openup is already running ($(cat ${_PID})):"
 | |
| 	pr_err "${_PID}"
 | |
| 	exit 1
 | |
| fi
 | |
| echo $$ >${_PID}
 | |
| 
 | |
| if [ -n "${nokrn}" ]; then
 | |
| 	EXCLUDE_PKG="binpatch${_REL_INT}-${_ARCH}-kernel ${EXCLUDE_PKG}"
 | |
| fi
 | |
| if [ -n "${EXCLUDE_PKG}" ]; then
 | |
| 	pr "Excluded package(s)/binpatch(es): ${EXCLUDE_PKG}"
 | |
| fi
 | |
| 
 | |
| check_openupd
 | |
| 
 | |
| if [ -z "${nosyspatch}" -a -e /usr/sbin/syspatch ]; then
 | |
| 	_sum=$(sha256 -q /usr/sbin/syspatch)
 | |
| 	case "${_sum}"
 | |
| 	in
 | |
| 		# 6.1 syspatch
 | |
| 		b8b9c4bec128884498b5c3e77a350f42079f7987ef862a1ec452f489403dfbe6)
 | |
| 			patch_syspatch
 | |
| 			;;
 | |
| 	esac
 | |
| fi
 | |
| 
 | |
| if [ "${checkrun}" ]; then
 | |
| 	if [ "${_REL_INT}" -ge 61 -a -z "${nosyspatch}" ]; then
 | |
| 		check_syspatches
 | |
| 	fi
 | |
| 	check_vuxml
 | |
| else
 | |
| 	[ -z "${nosig}" ] && get_key
 | |
| 	rm_old_bp
 | |
| 	if [ "${_REL_INT}" -lt 61 -a -z "${nosyspatch}" ]; then
 | |
| 		update_binpatches
 | |
| 	else
 | |
| 		[ -z "${nosyspatch}" ] && update_syspatches
 | |
| 	fi
 | |
| 	update_pkg
 | |
| fi
 | |
| 
 | |
| do_i_need_to_reboot
 | |
| 
 | |
| rm -rf ${_TMPDIR}
 | |
| rm ${_PID}
 | 
