freebsd-dev/usr.sbin/bsdconfig/share/mustberoot.subr
Devin Teske e14ddd1f16 Add new flags -d' (sets debug=1) and -D file' (sets debugFile) and
improve debugging initialization. Also fixup USAGE statements while we're
here. Also, change initialization of main program to _not_ change working
directory, allowing the debugFile to be relative without confusion.
2013-04-22 05:02:34 +00:00

429 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 msg hline size
[ "$( 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
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
msg=$( printf "$msg_you_are_not_root_but" bsdconfig )
hline="$hline_arrows_tab_enter"
size=$( eval f_dialog_menu_size \
\"\$DIALOG_TITLE\" \
\"\$DIALOG_BACKTITLE\" \
\"\$msg\" \
\"\$hline\" \
$menu_list )
local dialog_menu mtag retval
dialog_menu=$( eval $DIALOG \
--title \"\$DIALOG_TITLE\" \
--backtitle \"\$DIALOG_BACKTITLE\" \
--hline \"\$hline\" \
--ok-label \"\$msg_ok\" \
--cancel-label \"\$msg_cancel\" \
--menu \"\$msg\" $size \
$menu_list \
2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
)
retval=$?
setvar DIALOG_MENU_$$ "$dialog_menu"
mtag=$( f_dialog_menutag )
[ $retval -eq 0 ] || f_die
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.
#
msg="$msg_please_enter_password"
hline="$hline_alnum_punc_tab_enter"
size=$( f_dialog_inputbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$msg" \
"$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 "$msg" $size \
2>&1 > /dev/null )
retval=$?
# Catch X11-related errors
[ $retval -eq 255 ] &&
f_die $retval "$password"
else
local dialog_inputbox
dialog_inputbox=$( $DIALOG \
--title "$DIALOG_TITLE" \
--backtitle "$DIALOG_BACKTITLE" \
--hline "$hline" \
--ok-label "$msg_ok" \
--cancel-label "$msg_cancel" \
--insecure \
--passwordbox "$msg" $size \
2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
)
retval=$?
setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
password=$( f_dialog_inputstr )
fi
# Exit if the user cancelled.
[ $retval -eq $SUCCESS ] || exit $retval
#
# 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 size width height
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"
size=$( f_xdialog_2inputsbox_size \
"$DIALOG_TITLE" \
"$DIALOG_BACKTITLE" \
"$msg" \
"$field_username" "" \
"$field_password" "" )
width="${size##*[$IFS]}"
height="${size%%[$IFS]*}"
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