diff options
author | 2016-09-05 11:04:45 +0000 | |
---|---|---|
committer | 2016-09-05 11:04:45 +0000 | |
commit | cb6f7b6fe9a6fa7d5370ea9b77b8c9846086a8a5 (patch) | |
tree | d999269c573b83f6b0b5e99053e992bb950d3641 | |
parent | I was bound to forget some files ... (diff) | |
download | wireguard-openbsd-cb6f7b6fe9a6fa7d5370ea9b77b8c9846086a8a5.tar.xz wireguard-openbsd-cb6f7b6fe9a6fa7d5370ea9b77b8c9846086a8a5.zip |
Welcome syspatch(8), a binary patch management utility for the base system.
This is currently a POC, maybe it will become something, maybe not.
Therefore it will not be hooked to the build before we are happy with it.
Workflow would be something like:
- fetch and verify signed tarballs containing the patched binaries from a mirror
- create a rollback tarball of the files we are about to replace
- extract and install the patched files
*** BIG FAT RED DISCLAIMER ***
This is very much WIP, it does *NOT* work, don't bikeshed, don't use it!
"get it in" deraadt@
-rw-r--r-- | usr.sbin/syspatch/Makefile | 11 | ||||
-rw-r--r-- | usr.sbin/syspatch/syspatch.8 | 37 | ||||
-rw-r--r-- | usr.sbin/syspatch/syspatch.sh | 276 |
3 files changed, 324 insertions, 0 deletions
diff --git a/usr.sbin/syspatch/Makefile b/usr.sbin/syspatch/Makefile new file mode 100644 index 00000000000..216c8063525 --- /dev/null +++ b/usr.sbin/syspatch/Makefile @@ -0,0 +1,11 @@ +# $OpenBSD: Makefile,v 1.1 2016/09/05 11:04:45 ajacoutot Exp $ + +MAN= syspatch.8 + +SCRIPT= syspatch.sh + +realinstall: + ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${.CURDIR}/${SCRIPT} ${DESTDIR}${BINDIR}/syspatch + +.include <bsd.prog.mk> diff --git a/usr.sbin/syspatch/syspatch.8 b/usr.sbin/syspatch/syspatch.8 new file mode 100644 index 00000000000..7fe7e8b6af3 --- /dev/null +++ b/usr.sbin/syspatch/syspatch.8 @@ -0,0 +1,37 @@ +.\" $OpenBSD: syspatch.8,v 1.1 2016/09/05 11:04:45 ajacoutot Exp $ +.\" +.\" Copyright (c) 2016 Antoine Jacoutot <ajacoutot@openbsd.org> +.\" +.\" 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. +.\" +.Dd $Mdocdate: September 5 2016 $ +.Dt SYSPATCH 8 +.Os +.Sh NAME +.Nm syspatch +.Nd manage binary patches +.Sh SYNOPSIS +.Nm syspatch +.Op Fl c | l | r Ar patchname +.Sh DESCRIPTION +notyet +.Sh SEE ALSO +.Xr release 8 +.Sh HISTORY +.Nm +first appeared in +.Ox 6.1 . +.Sh AUTHORS +.Nm +was written by +.An Antoine Jacoutot Aq Mt ajacoutot@openbsd.org . diff --git a/usr.sbin/syspatch/syspatch.sh b/usr.sbin/syspatch/syspatch.sh new file mode 100644 index 00000000000..be7c9115f36 --- /dev/null +++ b/usr.sbin/syspatch/syspatch.sh @@ -0,0 +1,276 @@ +#!/bin/ksh +# +# $OpenBSD: syspatch.sh,v 1.1 2016/09/05 11:04:45 ajacoutot Exp $ +# +# Copyright (c) 2016 Antoine Jacoutot <ajacoutot@openbsd.org> +# +# 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. + +trap "syspatch_trap" 2 3 9 13 15 ERR + +set -e + +usage() +{ + echo "usage: ${0##*/} [-c | -l | -r patchname ]" >&2 && return 1 +} + +needs_root() +{ + [[ $(id -u) -eq 0 ]] || \ + (echo "${0##*/}: need root privileges"; return 1) +} + +syspatch_sort() +{ + local _p _patch _patches=$(</dev/stdin) + [[ -n ${_patches} ]] || return 0 # nothing to do + + for _patch in ${_patches}; do + echo ${_patch##*_} + done | sort -u | while read _p; do + echo "${_patches}" | \ + grep -E "syspatch-${_RELINT}-[0-9]{3}_${_p}" | \ + sort -V | tail -1 + done +} + +syspatch_trap() +{ + rm -rf ${_TMP} + exit 1 +} + +apply_patches() +{ + # XXX cleanup mismatch/old rollback patches and sig (installer should as well) + local _patch _patches="$@" + [[ -n ${_patches} ]] || return 0 # nothing to do + + install -d -m 0755 /var/syspatch/${_REL} + + for _patch in ${_patches}; do + fetch_and_verify "${_patch}" || return + install_patch "${_patch}" || return + done + + # XXX needed? -- non-fatal + mtree -qdef /etc/mtree/4.4BSD.dist -p / -U >/dev/null || true + mtree -qdef /etc/mtree/BSD.x11.dist -p / -U >/dev/null || true +} + +create_rollback() +{ + local _patch=$1 _type + [[ -n ${_patch} ]] + shift + local _files="${@}" + [[ -n ${_files} ]] + + _type=$(tar -tzf ${_TMP}/${_patch}.tgz bsd 2>/dev/null || echo userland) + + _files="$(echo ${_files} \ + | sed "s|var/syspatch/${_REL}/${_patch#syspatch-60-}.patch.sig||g")" + + (cd / && \ + if [[ ${_type} == bsd ]]; then + # XXX bsd.mp created twice in the tarball + if ${_BSDMP}; then + tar -czf /var/syspatch/${_REL}/rollback-${_patch}.tgz \ + -s '/^bsd$/bsd.mp/' -s '/^bsd.sp$/bsd/' \ + ${_files} bsd.sp 2>/dev/null || return # no /bsd.mp + else + tar -czf /var/syspatch/${_REL}/rollback-${_patch}.tgz \ + ${_files} || return + fi + else + tar -czf /var/syspatch/${_REL}/rollback-${_patch}.tgz \ + ${_files} || return + fi + ) +} + +fetch_and_verify() +{ + # XXX privsep ala installer + local _patch="$@" + [[ -n ${_patch} ]] + + local _key="/etc/signify/openbsd-${_RELINT}-syspatch.pub" _p + + ${_FETCH} -o "${_TMP}/SHA256.sig" "${PATCH_PATH}/SHA256.sig" + + for _p in ${_patch}; do + _p=${_p}.tgz + ${_FETCH} -mD "Get/Verify" -o "${_TMP}/${_p}" \ + "${PATCH_PATH}/${_p}" + (cd ${_TMP} && /usr/bin/signify -qC -p ${_key} -x SHA256.sig ${_p}) + done +} + +install_file() +{ + # XXX handle sym/hardlinks? + # XXX handle dir becoming file and vice-versa? + local _src=$1 _dst=$2 + [[ -f ${_src} && -f ${_dst} ]] + + local _fmode _fown _fgrp + eval $(stat -f "_fmode=%OMp%OLp _fown=%Su _fgrp=%Sg" \ + ${_src}) + + install -DFS -m ${_fmode} -o ${_fown} -g ${_fgrp} \ + ${_src} ${_dst} +} + +install_kernel() +{ + local _backup=false _bsd=/bsd _kern=$1 + [[ -n ${_kern} ]] + + # we only save the original release kernel once + [[ -f /bsd.rollback${_RELINT} ]] || _backup=true + + if ${_BSDMP}; then + [[ ${_kern##*/} == bsd ]] && _bsd=/bsd.sp + fi + + if ${_backup}; then + install -FSp /bsd /bsd.rollback${_RELINT} || return + fi + + if [[ -n ${_bsd} ]]; then + install -FS ${_kern} ${_bsd} || return + fi +} + +install_patch() +{ + local _explodir _file _files _patch="$1" + [[ -n ${_patch} ]] + + local _explodir=${_TMP}/${_patch} + mkdir -p ${_explodir} + + _files="$(tar xvzphf ${_TMP}/${_patch}.tgz -C ${_explodir})" + create_rollback ${_patch} "${_files}" + + for _file in ${_files}; do + # can't rely on _type, we need to install 001_foo.patch.sig + if [[ ${_file} == @(bsd|bsd.mp) ]]; then + if ! install_kernel ${_explodir}/${_file}; then + rollback_patch ${_patch} + return 1 + fi + else + if ! install_file ${_explodir}/${_file} /${_file}; then + rollback_patch ${_patch} + return 1 + fi + fi + done +} + +ls_avail() +{ + ${_FETCH} -o - "${PATCH_PATH}/index.txt" | sed 's/^.* //;s/^M//;s/.tgz$//' | \ + grep "^syspatch-${_RELINT}-.*$" | sort -V +} + +ls_installed() +{ + local _p + cd /var/syspatch/${_REL} && set -- * + for _p; do + [[ ${_p} = rollback-syspatch-${_RELINT}-*.tgz ]] && \ + _p=${_p#rollback-} && echo ${_p%.tgz} + done | sort -V +} + +ls_missing() +{ + local _a _installed + _installed="$(ls_installed)" + + for _a in $(ls_avail); do + if [[ -n ${_installed} ]]; then + echo ${_a} | grep -qw -- "${_installed}" || echo ${_a} + else + echo ${_a} + fi + done +} + +rollback_patch() +{ + local _explodir _file _files _patch=$1 _type + [[ -n ${_patch} ]] + + _type=$(tar -tzf /var/syspatch/${_REL}/rollback-${_patch}.tgz bsd \ + 2>/dev/null || echo userland) + + # make sure the syspatch is installed and is the latest version + echo ${_patch} | grep -qw -- "$(ls_installed | syspatch_sort)" + + _explodir=${_TMP}/rollback-${_patch} + mkdir -p ${_explodir} + + _files="$(tar xvzphf /var/syspatch/${_REL}/rollback-${_patch}.tgz -C ${_explodir})" + for _file in ${_files}; do + if [[ ${_type} == bsd ]]; then + install_kernel ${_explodir}/${_file} || return + else + install_file ${_explodir}/${_file} /${_file} || return + fi + done + + rm /var/syspatch/${_REL}/rollback-${_patch}.tgz \ + /var/syspatch/${_REL}/${_patch#syspatch-${_RELINT}-}.patch.sig +} + +# we do not run on current +set -A _KERNV -- $(sysctl -n kern.version | \ + sed 's/^OpenBSD \([0-9]\.[0-9]\)\([^ ]*\).*/\1 \2/;q') +[[ -z ${_KERNV[1]} ]] || [[ ${_KERNV[1]} == "-stable" ]] + +# check unallowed args (-ab, -a foo -b, -a -b) +[[ -z $@ || \ + $@ == @(|-[[:alnum:]]@(|+([[:blank:]])[!-]*([![:blank:]])))*([[:blank:]]) ]] || \ + usage + +# XXX to be discussed +[[ -n ${PATCH_PATH} ]] +[[ -d ${PATCH_PATH} ]] && PATCH_PATH="file://$(readlink -f ${PATCH_PATH})" + +_FETCH="/usr/bin/ftp -MV -k ${FTP_KEEPALIVE-0}" +_REL=${_KERNV[0]} +_RELINT=${_REL%\.*}${_REL#*\.} +_TMP=$(mktemp -d -p /tmp syspatch.XXXXXXXXXX) +[[ $(sysctl -n hw.ncpu) -gt 1 ]] && _BSDMP=true || _BSDMP=false + +while getopts clr: arg; do + case ${arg} in + c) ls_missing;; + l) ls_installed;; + r) needs_root && rollback_patch "${OPTARG}";; + *) usage;; + esac +done +shift $(( OPTIND -1 )) +[[ $# -ne 0 ]] && usage + +if [[ ${OPTIND} == 1 ]]; then + needs_root && apply_patches $(ls_missing) +fi + +rm -rf ${_TMP} |