doas/vidoas
Kimmo Suominen f2894542a6 vidoas: Address security concerns and improve some behaviour
I'm not claiming this script is now safe. It would certainly benefit
from additional review. I do think (and hope) that I did not make things
worse, at least.

It might be better to look at vipw(8) or visudo(8), which both are
written in C, for prior art on how to do this kind of thing securely.

Security changes:

- Exit on errors and if referencing unset variables.

- Set PATH so that we don't run unintended commands from the PATH that
  is in the caller's environment.

- Set umask to prevent other users from having write access to the
  temporary files.

- Use /var/tmp instead of /tmp, as /tmp is not shared between users on
  all systems. (So trying to install a file from /tmp as root would not
  find the file, if the user running vidoas is not root.)

  XXX: Using /var/tmp does not guarantee this either, but is more likely
  to work.

- Create a temporary file for editing and use ln(1) to acquire the lock.
  This addresses a race condition between checking for the lock file and
  creating it.

- Use "install -r" to avoid a truncated doas.conf from existing as would
  happen with cp (or install without the "-r" option).

  XXX: "install -r" is not portable.

- Use "install -m" to set the mode of the installed doas.conf file.

Changes to user experience:

- Don't check for executability of ${EDITOR} as it is not required to be
  an absolute path to the executable.

- Don't install an unchanged doas.conf file.

- Don't install an empty doas.conf file.

- The above two checks result in a no-op in the case that ${EDITOR}
  could not be run.

- Present the user with a choice of fixing errors or canceling changes.

- Output diagnostic messages to stderr (just like other tools do, e.g.
  doas, ln, and cp).

TODO:

- Avoid using hard-coded paths (/usr/local/bin and /usr/local/etc).
  They should be replaced with @PREFIX@/bin and @SYSCONFDIR@ before
  installing.
2020-10-22 11:23:56 +03:00

108 lines
1.9 KiB
Bash
Executable file

#!/bin/sh
# This script edits a temporary copy of the doas.conf file and
# automatically checks it for syntax errors before installing
# the new copy of doas.conf.
set -eu
PATH=/bin:/usr/bin:/usr/local/bin
export PATH
PROG="${0##*/}"
umask 077
WRK_DIR=/var/tmp
INSTALL_DIR=/usr/local/etc
doas_conf_mode=0644
doas_lock_file="${WRK_DIR}/doas.conf"
installed_doas="${INSTALL_DIR}/doas.conf"
die()
{
echo "${PROG}: ${@}" 1>&2
exit 1
}
warn()
{
echo "${PROG}: ${@}" 1>&2
}
get_intr()
{
stty -a \
| sed -En '
/^(.* )?intr = / {
s///
s/;.*$//
p
}
'
}
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
}
tmp_doas="$(mktemp "${WRK_DIR}/doas.conf.XXXXXXXXXX")"
set_trap_rm "${tmp_doas}"
# Check to see if an existing configuration file is installed.
if [ -f "${installed_doas}" ]
then
if [ -r "${installed_doas}" ]
then
cp "${installed_doas}" "${tmp_doas}"
else
die "Cannot read ${installed_doas}"
fi
fi
# Check to see if existing temporary doas.conf file exists.
if ln "${tmp_doas}" "${doas_lock_file}"
then
set_trap_rm "${tmp_doas}" "${doas_lock_file}"
else
die "The doas.conf file is already locked"
fi
"${EDITOR:-vi}" "${tmp_doas}" || true
while ! doas -C "${tmp_doas}"
do
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
done
if [ -s "${tmp_doas}" ]
then
if cmp -s "${tmp_doas}" "${installed_doas}"
then
warn "No changes made"
warn "${installed_doas} unchanged"
else
doas install -r -m "${doas_conf_mode}" \
"${tmp_doas}" "${installed_doas}" \
&& warn "${installed_doas} updated"
fi
else
warn "Not installing an empty doas.conf file"
warn "${installed_doas} unchanged"
fi