diff options
Diffstat (limited to 'vidoas')
| -rwxr-xr-x | vidoas | 173 | 
1 files changed, 173 insertions, 0 deletions
| @@ -0,0 +1,173 @@ +#!/bin/sh + +# Copyright (c) 2020 Kimmo Suominen <kim@netbsd.org> +# Copyright (c) 2022 Thomas Voss <mail@thomasvoss.com> +# +# 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. + +# Edit a temporary copy of the doas.conf file and check it for syntax +# errors before installing it as the actual doas.conf file. + +set -eu + +PATH=/bin:/usr/bin:/usr/local/bin +export PATH + +PROG="${0##*/}" + +umask 022 + +DOAS_CONF=@DOAS_CONF@ +doas_conf_mode="0600" + +warn() { echo "$PROG: $@" >&2; } +die()  { rv=$1; shift; warn "$@"; exit $rv; } + +get_intr() { +	stty -a | sed -En ' +		/^(.* )?intr = / { +			s/// +			s/;.*$// +			p +		}' +} + +set_trap_rm() { +	local file files +	files= +	for file in "$@"; do +		files="$files '$file'" +	done +	[ -n "$files" ] && trap "rm -f $files" EXIT HUP INT TERM +} + +noop=0 + +while getopts hn c; do +	case "$c" in +	n) +		noop=$(($noop + 1)) +		;; +	*) +		echo "Usage: $PROG [-n] [file]" >&2; +		exit 1 +		;; +	esac +done +shift $(($OPTIND - 1)) + +case $# in +0)                    ;; +1) DOAS_CONF="$1"     ;; +*) usage 1>&2; exit 1 ;; +esac + +case ${noop} in +0) noop=false                      ;; +1) noop=true                       ;; +*) noop=true; exec >/dev/null 2>&1 ;; +esac + +case "$DOAS_CONF" in +-*) +	warn "Invalid filename: $DOAS_CONF" +	die 1 "Try using './$DOAS_CONF' instead" +	;; +esac + +doas_conf_dir="$(dirname "$DOAS_CONF")" +doas_conf_base="$(basename "$DOAS_CONF")" +DOAS_CONF="$doas_conf_dir/$doas_conf_base" +doas_lock_file="$DOAS_CONF.lck" + +# These checks are only for producing nicer diagnostic messages to the +# user.  They are not relied on by the rest of the code. + +[ ! -e "$doas_conf_dir" ] && die 1 "$doas_conf_dir does not exist" +[ ! -d "$doas_conf_dir" ] && die 1 "$doas_conf_dir is not a directory" +[ ! -w "$doas_conf_dir" ] && { +	owner="$(stat -c %U "$doas_conf_dir")" +	warn "$doas_conf_dir is not writable" +	die 1 "You probably need to run $PROG as $owner" +} + +tmp_doas="$(mktemp --tmpdir vidoas.XXXXXXXXXX)" \ +	|| die 1 "You probably need to run $PROG as root" +set_trap_rm "$tmp_doas" + +# It is important that the ln(1) command fails if the target already +# exists.  Some versions are known to behave like "ln -f" by default +# (removing any existing target).  Adjust PATH to avoid such ln(1) +# implementations. + +tmp_test_ln="$(mktemp --tmpdir vidoas.XXXXXXXXXX)" +set_trap_rm "$tmp_doas" "$tmp_test_ln" + +ln "$tmp_doas" "$tmp_test_ln" 2>/dev/null \ +	&& die 1 'ln(1) is not safe for creating lock files, bailing' + +# If a doas.conf file exists, copy it into the temporary file for +# editing.  If none exist, the editor will open with an empty file. + +[ -f "$DOAS_CONF" ] && { +	if [ -r "$DOAS_CONF" ]; then +		cp "$DOAS_CONF" "$tmp_doas" +	else +		die 1 "$DOAS_CONF is not readable" +	fi +} + +$noop && { +	doas -C "$DOAS_CONF" || die 1 "$DOAS_CONF contains syntax errors." +	die 0 'OK: Prerequisite checks passed' +} + +# Link the temporary file to the lock file. + +if ln "$tmp_doas" "$doas_lock_file"; then +	set_trap_rm "$tmp_doas" "$tmp_test_ln" "$doas_lock_file" +else +	die 1 "$DOAS_CONF is already locked" +fi + +# Some versions of vi(1) exit with a code that reflects the number of +# editing errors made.  This is why we ignore the exit code from the +# editor. + +doasedit "$tmp_doas" || true + +until doas -C "$tmp_doas"; do +	warn "Press enter to edit doas.conf again to fix it," +	warn "or ($(get_intr)) to cancel." +	read _ +	doasedit "$tmp_doas" || true +done + +# Use mv(1) to rename the temporary file to doas.conf as it is atomic. +# Update: No longer use mv as it messes up permissions on the doas.conf file. +# Use install with ownership set to root. + +if [ -s "$tmp_doas" ]; then +	if cmp -s "$tmp_doas" "$DOAS_CONF"; then +		warn "No changes made" +		warn "$DOAS_CONF unchanged" +	else +		install -o root -m "$doas_conf_mode" "$tmp_doas" \ +			"$DOAS_CONF" \ +			&& warn "$DOAS_CONF updated" +	fi +else +	warn "Not installing an empty doas.conf file" +	warn "$DOAS_CONF unchanged" +fi |