freebsd-dev/usr.sbin/bsdconfig/share/mustberoot.subr
Devin Teske 052f89691c Standardize the way functions build their arguments leading up to a dialog
invocation. Specifically, "top-load" your arguments and in the order in-
which they will be displayed. For example, many [if not all] widgets display
information in the following order, top-to-bottom (visually):

+ backtitle (displayed behind the widget at top-left)
+ title (at the top of the `window')
+ prompt text (just below the title and above whatever widget you choose)
+ Depending on widget, _one_ of the following:
  - menu list
  - radio list
  - check list
  - text input box with initial text
  - [Xdialog(1)] 2x or 3x text input boxes
  - [dialog(1)] a multi-part form
  - progress bar
  - etc. (many more widget choices)
+ buttons (right below the selected widget)
+ [dialog(1)] the hline (displayed at bottom of `window')

NOTE: Xdialog(1) accepts and silently ignores --hline

When building local arguments for your dialog invocation, if the value can't
be cleanly loaded into a local, add "# Calculated below" to the end of the
local declaration while retaining the block order of argument declarations.

Move other local declarations that are not associated with this top-loading
the dialog arguments to right-above where they are first-used.

Also, standardize on the names of the arguments. For example, always use
$prompt (instead of sometimes $msg and sometimes $prompt); use $menu_list
or $shell_list or $radio_list for those respective widgets; ad nauseum.

While we're doing this, flush-out full arguments for many invocations that
were passing NULL strings (making it unapparent if you were staring at this
one invocation what argument that NULL string was supposed to represent).

Last, while we're in startup/rcconf let's remove the unnecessary use of a
GLOBAL (RCCONF_MENU_LIST) for the menu_list.
2013-06-02 20:02:50 +00:00

421 lines
12 KiB
Plaintext

if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
#
# Copyright (c) 2006-2013 Devin Teske
# All Rights Reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
############################################################ INCLUDES
BSDCFG_SHARE="/usr/share/bsdconfig"
. $BSDCFG_SHARE/common.subr || exit 1
f_dprintf "%s: loading includes..." mustberoot.subr
f_include $BSDCFG_SHARE/dialog.subr
BSDCFG_LIBE="/usr/libexec/bsdconfig"
f_include_lang $BSDCFG_LIBE/include/messages.subr
############################################################ CONFIGURATION
# NOTE: These are not able to be overridden/inherited for security purposes.
#
# Number of tries a user gets to enter his/her password before we log the
# sudo(8) failure and exit.
#
PASSWD_TRIES=3
#
# While in SECURE mode, should authentication as `root' be allowed? Set to
# non-NULL to enable authentication as `root', otherwise disabled.
#
# WARNING:
# Unless using a custom sudo(8) configuration, user `root' should not be
# allowed because no password is required to become `root' when already `root'
# and therefore, any value entered as password will work.
#
SECURE_ALLOW_ROOT=
#
# While in SECURE mode, should we divulge (through error message) when the
# requested authentication user does not exist? Set to non-NULL to enable,
# otherwise a non-existent user is treated like an invalid password.
#
SECURE_DIVULGE_UNKNOWN_USER=
############################################################ FUNCTIONS
# f_become_root_via_sudo
#
# If not running as root, prompt for sudo(8) credentials to become root.
# Re-execution of the current program via sudo is automatically handled.
#
# The following environment variables effect functionality:
#
# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate
# that Xdialog(1) should be used instead of dialog(1).
#
f_become_root_via_sudo()
{
local prompt hline height width rows msg
[ "$( id -u )" = "0" ] && return $SUCCESS
f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
#
# Ask the user if it's OK to become root via sudo(8) and give them
# the option to save this preference (by touch(1)ing a file in the
# user's $HOME directory).
#
local checkpath="${HOME%/}/.bsdconfig_uses_sudo"
if [ ! -e "$checkpath" ]; then
prompt=$( printf "$msg_you_are_not_root_but" bsdconfig )
msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" )
local menu_list="
'X' '$msg_cancel_exit'
'1' '$msg'
'2' '$msg_try_sudo_only_this_once'
" # END-QUOTE
hline="$hline_arrows_tab_enter"
eval f_dialog_menu_size height width rows \
\"\$DIALOG_TITLE\" \
\"\$DIALOG_BACKTITLE\" \
\"\$prompt\" \
\"\$hline\" \
$menu_list
local mtag
mtag=$( eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--ok-label \"\$msg_ok\" \
--cancel-label \"\$msg_cancel\" \
--menu \"\$prompt\" \
$height $width $rows \
$menu_list \
2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
) || f_die
f_dialog_data_sanitize mtag
case "$mtag" in
X) # Cancel/Exit
f_die ;;
1) # Always try sudo(8) when run as $user
local err
if ! err=$( touch "$checkpath" 2>&1 ); then
f_dialog_msgbox "$err"
else
f_show_msg "$msg_created_path" "$checkpath"
fi
esac
else
#
# This user has created the path signing-off on sudo(8)-use
# but let's still give them a short/quick/unobtrusive reminder
#
f_dialog_info "$msg_becoming_root_via_sudo"
[ "$USE_XDIALOG" ] || sleep 0.6
fi
#
# Check sudo(8) access before prompting for password.
#
:| sudo -S -v 2> /dev/null
if [ $? -ne $SUCCESS ]; then
#
# sudo(8) access denied. Prompt for their password.
#
prompt="$msg_please_enter_password"
hline="$hline_alnum_punc_tab_enter"
f_dialog_inputbox_size height width \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$prompt" \
"$hline"
#
# Continue prompting until they either Cancel, succeed
# or exceed the number of allowed failures.
#
local password nfailures=0 retval
while [ $nfailures -lt $PASSWD_TRIES ]; do
if [ "$USE_XDIALOG" ]; then
password=$( $DIALOG \
--title "$DIALOG_TITLE" \
--backtitle "$DIALOG_BACKTITLE" \
--hline "$hline" \
--ok-label "$msg_ok" \
--cancel-label "$msg_cancel" \
--password --inputbox "$prompt" \
$height $width \
2>&1 > /dev/null )
retval=$?
# Catch X11-related errors
[ $retval -eq 255 ] &&
f_die $retval "$password"
else
password=$( $DIALOG \
--title "$DIALOG_TITLE" \
--backtitle "$DIALOG_BACKTITLE" \
--hline "$hline" \
--ok-label "$msg_ok" \
--cancel-label "$msg_cancel" \
--insecure \
--passwordbox "$prompt" \
$height $width \
2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
) || exit $?
fi
debug= f_dialog_line_sanitize password
#
# Validate sudo(8) credentials
#
sudo -S -v 2> /dev/null <<-EOF
$password
EOF
retval=$?
unset password # scrub memory
if [ $retval -eq $SUCCESS ]; then
# Access granted...
break
else
# Access denied...
nfailures=$(( $nfailures + 1 ))
# introduce a short delay
if [ $nfailures -lt $PASSWD_TRIES ]; then
f_dialog_info "$msg_sorry_try_again"
sleep 1
fi
fi
done
#
# If user exhausted number of allowed password tries, log
# the security event and exit immediately.
#
if [ $nfailures -ge $PASSWD_TRIES ]; then
msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
logger -p auth.notice -t sudo " " \
"$USER : $msg" \
"; TTY=$(tty)" \
"; PWD=$PWD" \
"; USER=root" \
"; COMMAND=$0"
f_die 1 "sudo: $msg"
fi
fi
# Use xauth(1) to grant root the ability to use this X11/SSH session
if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then
f_have xauth || f_die 1 \
"$msg_no_such_file_or_directory" "$pgm" "xauth"
local HOSTNAME displaynum
HOSTNAME=$(hostname)
displaynum="${DISPLAY#*:}"
xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \
$HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \
~root/.Xauthority merge - > /dev/null 2>&1'
fi
# Re-execute ourselves with sudo(8)
f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr
if [ $ARGC -gt 0 ]; then
exec sudo "$0" $ARGV
else
exec sudo "$0"
fi
exit $? # Never reached unless error
}
# f_authenticate_some_user
#
# Only used if running as root and requires X11 (see USE_XDIALOG below).
# Prompts the user to enter a username and password to be authenticated via
# sudo(8) to proceed.
#
# The following environment variables effect functionality:
#
# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate
# that Xdialog(1) should be used instead of dialog(1).
#
f_authenticate_some_user()
{
local msg hline height width
f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
#
# Secure-mode has been requested.
#
[ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11"
[ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root"
#
# Prompt for sudo(8) credentials.
#
msg="$msg_please_enter_username_password"
hline="$hline_alnum_punc_tab_enter"
f_xdialog_2inputsbox_size height width \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$msg" \
"$field_username" "" \
"$field_password" ""
height=$(( $height + 2 )) # Add height for --password
#
# Continue prompting until they either Cancel, succeed or exceed the
# number of allowed failures.
#
local user_pass nfailures=0 retval
while [ $nfailures -lt $PASSWD_TRIES ]; do
user_pass=$( $DIALOG \
--title "$DIALOG_TITLE" \
--backtitle "$DIALOG_BACKTITLE" \
--hline "$hline" \
--ok-label "$msg_ok" \
--cancel-label "$msg_cancel" \
--password --2inputsbox "$msg" \
$height $width \
"$field_username" "" \
"$field_password" "" \
2>&1 > /dev/null )
retval=$?
# Catch X11-related errors
[ $retval -eq 255 ] && f_die $retval "$user_pass"
# Exit if the user cancelled.
[ $retval -eq $SUCCESS ] || exit $retval
#
# Make sure the user exists and is non-root
#
local user password
user="${user_pass%%/*}"
password="${user_pass#*/}"
unset user_pass # scrub memory
if [ ! "$user" ]; then
nfailures=$(( $nfailures + 1 ))
f_dialog_msgbox "$msg_no_username"
continue
fi
if [ ! "$SECURE_ALLOW_ROOT" ]; then
case "$user" in
root|toor)
nfailures=$(( $nfailures + 1 ))
f_show_msg "$msg_user_disallowed" "$user"
continue
esac
fi
if ! f_quietly id "$user"; then
nfailures=$(( $nfailures + 1 ))
if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
f_show_msg "$msg_unknown_user" "$user"
elif [ $nfailures -lt $PASSWD_TRIES ]; then
f_dialog_info "$msg_sorry_try_again"
sleep 1
fi
continue
fi
#
# Validate sudo(8) credentials for given user
#
su -m "$user" <<-EOF
sh <<EOS
sudo -k
sudo -S -v 2> /dev/null <<EOP
$password
EOP
EOS
EOF
retval=$?
unset user
unset password # scrub memory
if [ $retval -eq $SUCCESS ]; then
# Access granted...
break
else
# Access denied...
nfailures=$(( $nfailures + 1 ))
# introduce a short delay
if [ $nfailures -lt $PASSWD_TRIES ]; then
f_dialog_info "$msg_sorry_try_again"
sleep 1
fi
fi
done
#
# If user exhausted number of allowed password tries, log
# the security event and exit immediately.
#
if [ $nfailures -ge $PASSWD_TRIES ]; then
msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
logger -p auth.notice -t sudo " " \
"${SUDO_USER:-$USER} : $msg" \
"; TTY=$(tty)" \
"; PWD=$PWD" \
"; USER=root" \
"; COMMAND=$0"
f_die 1 "sudo: $message"
fi
}
# f_mustberoot_init
#
# If not already root, make the switch to root by re-executing ourselves via
# sudo(8) using user-supplied credentials.
#
# The following environment variables effect functionality:
#
# SECURE Either NULL or Non-NULL. If given a value will indicate
# that (while running as root) sudo(8) authentication is
# required to proceed.
#
f_mustberoot_init()
{
if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then
f_become_root_via_sudo
elif [ "$SECURE" ]; then
f_authenticate_some_user
fi
}
############################################################ MAIN
f_dprintf "%s: Successfully loaded." mustberoot.subr
fi # ! $_MUSTBEROOT_SUBR