2020-08-07 21:18:40 +00:00
|
|
|
#!/bin/sh
|
|
|
|
|
2020-11-09 09:43:46 +00:00
|
|
|
# Copyright (c) 2020 Kimmo Suominen <kim@netbsd.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.
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
# Edit a temporary copy of the doas.conf file and check it for syntax
|
|
|
|
# errors before installing it as the actual doas.conf file.
|
2020-08-07 21:42:52 +00:00
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
set -eu
|
2020-08-07 21:18:40 +00:00
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
PATH=/bin:/usr/bin:/usr/local/bin
|
|
|
|
export PATH
|
|
|
|
|
|
|
|
PROG="${0##*/}"
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
umask 022
|
2020-10-22 08:23:56 +00:00
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
DOAS_CONF=@DOAS_CONF@
|
2021-01-25 18:25:28 +00:00
|
|
|
doas_conf_mode="0600"
|
2020-10-22 08:23:56 +00:00
|
|
|
|
|
|
|
die()
|
|
|
|
{
|
|
|
|
echo "${PROG}: ${@}" 1>&2
|
|
|
|
exit 1
|
|
|
|
}
|
|
|
|
|
|
|
|
warn()
|
|
|
|
{
|
|
|
|
echo "${PROG}: ${@}" 1>&2
|
|
|
|
}
|
|
|
|
|
|
|
|
get_intr()
|
|
|
|
{
|
|
|
|
stty -a \
|
|
|
|
| sed -En '
|
|
|
|
/^(.* )?intr = / {
|
|
|
|
s///
|
|
|
|
s/;.*$//
|
|
|
|
p
|
|
|
|
}
|
|
|
|
'
|
|
|
|
}
|
2020-08-07 21:18:40 +00:00
|
|
|
|
2020-11-09 09:43:46 +00:00
|
|
|
owner_of()
|
|
|
|
{
|
|
|
|
local file
|
|
|
|
file="${1}"
|
|
|
|
|
|
|
|
if stat --version >/dev/null 2>&1
|
|
|
|
then
|
|
|
|
stat -c '%U' "${file}"
|
|
|
|
else
|
|
|
|
stat -f '%Su' "${file}"
|
|
|
|
fi \
|
|
|
|
| awk '{print $1; exit}'
|
|
|
|
}
|
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
set_trap_rm()
|
|
|
|
{
|
|
|
|
local file file_list
|
|
|
|
file_list=
|
|
|
|
for file
|
|
|
|
do
|
|
|
|
file_list="${file_list} '${file}'"
|
|
|
|
done
|
|
|
|
if [ -n "${file_list}" ]
|
|
|
|
then
|
|
|
|
trap "rm -f ${file_list}" 0 1 2 15
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2020-11-09 09:43:46 +00:00
|
|
|
usage()
|
|
|
|
{
|
|
|
|
cat <<EOF
|
|
|
|
Usage: ${PROG} [-n] [file]
|
|
|
|
${PROG} -h
|
|
|
|
|
|
|
|
Edit a temporary copy of a doas configuration file and check it for
|
|
|
|
syntax errors before installing it as the actual configuration file.
|
|
|
|
|
|
|
|
When no file is named, ${PROG} will edit the default configuration file
|
|
|
|
for doas(1): @DOAS_CONF@
|
|
|
|
|
|
|
|
Options:
|
|
|
|
-h Show this usage.
|
|
|
|
-n Do not edit the file, just perform prerequisite checks. If this
|
|
|
|
switch is repeated, all output will be suppressed and the check
|
|
|
|
result is only indicated by the exit status.
|
|
|
|
EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
noop=0
|
|
|
|
|
2020-11-09 09:54:04 +00:00
|
|
|
while getopts hn opt
|
2020-11-09 09:43:46 +00:00
|
|
|
do
|
|
|
|
case "${opt}" in
|
|
|
|
h) usage; exit 0;;
|
2020-11-09 09:54:04 +00:00
|
|
|
n) noop=$((${noop} + 1));;
|
2020-11-09 09:43:46 +00:00
|
|
|
*) usage 1>&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 "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.
|
|
|
|
|
|
|
|
if [ ! -e "${doas_conf_dir}" ]
|
|
|
|
then
|
|
|
|
die "${doas_conf_dir} does not exist"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ ! -d "${doas_conf_dir}" ]
|
|
|
|
then
|
|
|
|
die "${doas_conf_dir} is not a directory"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [ ! -w "${doas_conf_dir}" ]
|
|
|
|
then
|
|
|
|
owner="$(owner_of "${doas_conf_dir}")"
|
|
|
|
warn "${doas_conf_dir} is not writable"
|
|
|
|
die "You probably need to run ${PROG} as ${owner:-root}"
|
|
|
|
fi
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
tmp_doas="$(mktemp "${DOAS_CONF}.XXXXXXXXXX")" \
|
|
|
|
|| die "You probably need to run ${PROG} as root"
|
2020-10-22 08:23:56 +00:00
|
|
|
set_trap_rm "${tmp_doas}"
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
# 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 "${DOAS_CONF}.XXXXXXXXXX")"
|
|
|
|
set_trap_rm "${tmp_doas}" "${tmp_test_ln}"
|
|
|
|
|
|
|
|
if ln "${tmp_doas}" "${tmp_test_ln}" 2>/dev/null
|
|
|
|
then
|
2020-11-09 09:43:46 +00:00
|
|
|
die 'ln(1) is not safe for creating lock files, bailing'
|
2020-11-08 22:12:50 +00:00
|
|
|
fi
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
if [ -f "${DOAS_CONF}" ]
|
2020-08-07 21:18:40 +00:00
|
|
|
then
|
2020-11-08 22:12:50 +00:00
|
|
|
if [ -r "${DOAS_CONF}" ]
|
2020-10-22 08:23:56 +00:00
|
|
|
then
|
2020-11-08 22:12:50 +00:00
|
|
|
cp "${DOAS_CONF}" "${tmp_doas}"
|
2020-10-22 08:23:56 +00:00
|
|
|
else
|
2020-11-09 09:43:46 +00:00
|
|
|
die "${DOAS_CONF} is not readable"
|
2020-10-22 08:23:56 +00:00
|
|
|
fi
|
2020-08-07 21:18:40 +00:00
|
|
|
fi
|
|
|
|
|
2020-11-09 09:43:46 +00:00
|
|
|
if ${noop}
|
|
|
|
then
|
2020-11-09 15:30:49 +00:00
|
|
|
if ! doas -C "${DOAS_CONF}"
|
|
|
|
then
|
|
|
|
die "${DOAS_CONF} contains syntax errors."
|
|
|
|
fi
|
2020-11-09 09:43:46 +00:00
|
|
|
warn 'OK: Prerequisite checks passed'
|
|
|
|
exit 0
|
|
|
|
fi
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
# Link the temporary file to the lock file.
|
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
if ln "${tmp_doas}" "${doas_lock_file}"
|
2020-08-07 21:18:40 +00:00
|
|
|
then
|
2020-11-08 22:12:50 +00:00
|
|
|
set_trap_rm "${tmp_doas}" "${tmp_test_ln}" "${doas_lock_file}"
|
2020-10-22 08:23:56 +00:00
|
|
|
else
|
2020-11-08 22:12:50 +00:00
|
|
|
die "${DOAS_CONF} is already locked"
|
2020-08-07 21:18:40 +00:00
|
|
|
fi
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
# 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.
|
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
"${EDITOR:-vi}" "${tmp_doas}" || true
|
2020-08-07 21:18:40 +00:00
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
while ! doas -C "${tmp_doas}"
|
2020-08-07 21:18:40 +00:00
|
|
|
do
|
2020-10-22 08:23:56 +00:00
|
|
|
warn "Press enter to edit doas.conf again to fix it,"
|
|
|
|
warn "or interrupt ($(get_intr)) to cancel."
|
|
|
|
read status
|
|
|
|
"${EDITOR:-vi}" "${tmp_doas}" || true
|
2020-08-07 21:18:40 +00:00
|
|
|
done
|
|
|
|
|
2020-11-08 22:12:50 +00:00
|
|
|
# Use mv(1) to rename the temporary file to doas.conf as it is atomic.
|
2021-01-25 18:14:21 +00:00
|
|
|
# Update: No longer use mv as it messes up permissions on the doas.conf file.
|
|
|
|
# Use install with ownership set to root.
|
2020-11-08 22:12:50 +00:00
|
|
|
|
2020-10-22 08:23:56 +00:00
|
|
|
if [ -s "${tmp_doas}" ]
|
|
|
|
then
|
2020-11-08 22:12:50 +00:00
|
|
|
if cmp -s "${tmp_doas}" "${DOAS_CONF}"
|
2020-10-22 08:23:56 +00:00
|
|
|
then
|
|
|
|
warn "No changes made"
|
2020-11-08 22:12:50 +00:00
|
|
|
warn "${DOAS_CONF} unchanged"
|
2020-10-22 08:23:56 +00:00
|
|
|
else
|
2021-01-25 18:25:28 +00:00
|
|
|
install -o root -m "${doas_conf_mode}" \
|
|
|
|
"${tmp_doas}" "${DOAS_CONF}" \
|
|
|
|
&& warn "${DOAS_CONF} updated"
|
2020-10-22 08:23:56 +00:00
|
|
|
fi
|
|
|
|
else
|
|
|
|
warn "Not installing an empty doas.conf file"
|
2020-11-08 22:12:50 +00:00
|
|
|
warn "${DOAS_CONF} unchanged"
|
2020-10-22 08:23:56 +00:00
|
|
|
fi
|