freebsd-dev/usr.sbin/bsdconfig/share/mustberoot.subr
Devin Teske d4ae33f072 Performance and debugging enhancements:
+ Remove UNAME_P=$(...) from startup/misc -- already supplied by common.subr
+ Use f_getvar instead of $(eval echo \$$var) -- f_getvar is sub-shell free
+ Add `-e' and `-k var' options to f_eval_catch -- increasing use-cases
+ Use f_eval_catch to display errors on failure -- reducing duplicated code
+ Use f_eval_catch when we need output from a command -- improving debugging
+ Optimize f_isinter of strings.subr for performance -- now sub-shell free
+ Improve error checking on pidfiles -- using f_eval_catch and f_isinteger
+ Use $var_to_set arg of f_ifconfig_{inet,netmask} -- eliminate sub-shells
+ Use f_sprintf instead of $(printf ...) -- consolidate sub-shells
+ Use $var_to_set arg of f_route_get_default -- eliminate sub-shells
+ Add f_count to replace $(set -- ...;echo $#) -- eliminate sub-shells
+ Add f_count_ifs to replace $(IFS=x;set -- ...;echo $#) -- no sub-shells
+ Replace var="$var${var:+ }..." in loops with var="$var ..." with a follow-
  up var="${var# }" to trim leading whitespace -- optimize loops
+ Use $var_to_set arg of f_resolv_conf_nameservers -- eliminate sub-shells
+ Comments for the f_eval_catch function
+ Remove a duplicate `local ... desc ...' in f_device_get_all of device.subr
+ Use $var_to_set arg of f_device_capacity -- eliminate sub-shells
+ Whitespace fixes in f_dialog_init of dialog.subr
+ Optimize f_inet_atoi of media/tcpip.subr for performance -- sub-shell free
+ In several cases, send stderr to /dev/null -- clean up runtime execution
+ Change f_err of common.subr to go to program stderr not terminal stderr,
  allowing redirection of output from functions that use f_err
+ Disable debugging when using f_getvar to get variable argument to
  f_startup_rcconf_map_expand of startup/rcconf.subr
+ Use f_replace_all instead of $(echo ... | tr | sed) -- performance
+ Add a $var_to_set option to f_index_{file,menusel_{command,keyword}} of
  common.subr -- centralize sub-shells
2013-12-07 00:31:01 +00:00

425 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 (INCLUDING, 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
f_include $BSDCFG_SHARE/strings.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 funcname=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
f_sprintf prompt "$msg_you_are_not_root_but" bsdconfig
f_sprintf msg "$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
f_eval_catch $funcname touch \
'touch "%s"' "$checkpath" &&
f_show_msg "$msg_created_path" "$checkpath"
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
if [ $retval -eq $DIALOG_ESC ]; then
f_die $retval "$password"
elif [ $retval -ne $DIALOG_OK ]; then
# User cancelled
exit $retval
fi
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
f_sprintf msg "$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 $DIALOG_ESC ] && f_die $retval "$user_pass"
# Exit if the user cancelled.
[ $retval -eq $DIALOG_OK ] || 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_show_msg "$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
f_sprintf msg "$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