mustberoot.subr revision 238438
167754Smsmithif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 267754Smsmith# 377424Smsmith# Copyright (c) 2006-2012 Devin Teske 482367Smsmith# All Rights Reserved. 567754Smsmith# 667754Smsmith# Redistribution and use in source and binary forms, with or without 767754Smsmith# modification, are permitted provided that the following conditions 867754Smsmith# are met: 967754Smsmith# 1. Redistributions of source code must retain the above copyright 1067754Smsmith# notice, this list of conditions and the following disclaimer. 1167754Smsmith# 2. Redistributions in binary form must reproduce the above copyright 1271867Smsmith# notice, this list of conditions and the following disclaimer in the 1370243Smsmith# documentation and/or other materials provided with the distribution. 1467754Smsmith# 1567754Smsmith# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1667754Smsmith# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE 1767754Smsmith# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1867754Smsmith# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1967754Smsmith# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2067754Smsmith# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2167754Smsmith# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2267754Smsmith# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2367754Smsmith# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2467754Smsmith# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2567754Smsmith# SUCH DAMAGE. 2667754Smsmith# 2767754Smsmith# $FreeBSD: head/usr.sbin/bsdconfig/include/mustberoot.subr 238438 2012-07-14 03:16:57Z dteske $ 2867754Smsmith# 2967754Smsmith############################################################ INCLUDES 3067754Smsmith 3167754SmsmithBSDCFG_LIBE="/usr/libexec/bsdconfig" 3267754Smsmith. $BSDCFG_LIBE/include/common.subr || exit 1 3367754Smsmithf_include $BSDCFG_LIBE/include/dialog.subr 3467754Smsmithf_include_lang $BSDCFG_LIBE/include/messages.subr 3567754Smsmith 3667754Smsmith############################################################ CONFIGURATION 3767754Smsmith# NOTE: These are not able to be overridden/inherited for security purposes. 3867754Smsmith 3967754Smsmith# 4067754Smsmith# Number of tries a user gets to enter his/her password before we log the 4167754Smsmith# sudo(8) failure and exit. 4267754Smsmith# 4367754SmsmithPASSWD_TRIES=3 4467754Smsmith 4567754Smsmith# 4667754Smsmith# While in SECURE mode, should authentication as `root' be allowed? Set to 4767754Smsmith# non-NULL to enable authentication as `root', otherwise disabled. 4867754Smsmith# 4967754Smsmith# WARNING: 5067754Smsmith# Unless using a custom sudo(8) configuration, user `root' should not be 5167754Smsmith# allowed because no password is required to become `root' when already `root' 5267754Smsmith# and therefore, any value entered as password will work. 5367754Smsmith# 5467754SmsmithSECURE_ALLOW_ROOT= 5567754Smsmith 5667754Smsmith# 5767754Smsmith# While in SECURE mode, should we divulge (through error message) when the 5867754Smsmith# requested authentication user does not exist? Set to non-NULL to enable, 5967754Smsmith# otherwise a non-existent user is treated like an invalid password. 6067754Smsmith# 6167754SmsmithSECURE_DIVULGE_UNKNOWN_USER= 6267754Smsmith 6367754Smsmith############################################################ FUNCTIONS 6467754Smsmith 6567754Smsmith# f_become_root_via_sudo 6667754Smsmith# 6767754Smsmith# If not running as root, prompt for sudo(8) credentials to become root. 6867754Smsmith# Re-execution of the current program via sudo is automatically handled. 6967754Smsmith# 7067754Smsmith# The following environment variables effect functionality: 7167754Smsmith# 7267754Smsmith# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 7367754Smsmith# that Xdialog(1) should be used instead of dialog(1). 7467754Smsmith# 7567754Smsmithf_become_root_via_sudo() 7667754Smsmith{ 7767754Smsmith local msg hline size 7867754Smsmith 7967754Smsmith [ "$( id -u )" = "0" ] && return $SUCCESS 8067754Smsmith 8167754Smsmith f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 8267754Smsmith 8367754Smsmith # 8467754Smsmith # Check sudo(8) access before prompting for password. 8567754Smsmith # 8667754Smsmith :| sudo -S -v 2> /dev/null 8767754Smsmith if [ $? -ne $SUCCESS ]; then 8867754Smsmith # 8967754Smsmith # sudo(8) access denied. Prompt for their password. 9067754Smsmith # 9167754Smsmith msg="$msg_please_enter_password" 9267754Smsmith hline="$hline_alnum_punc_tab_enter" 9367754Smsmith size=$( f_dialog_inputbox_size \ 9467754Smsmith "$DIALOG_TITLE" \ 9567754Smsmith "$DIALOG_BACKTITLE" \ 9667754Smsmith "$msg" \ 9767754Smsmith "$hline" ) 9867754Smsmith 9967754Smsmith # 10067754Smsmith # Continue prompting until they either Cancel, succeed 10167754Smsmith # or exceed the number of allowed failures. 10267754Smsmith # 10367754Smsmith local password nfailures=0 retval 10467754Smsmith while [ $nfailures -lt $PASSWD_TRIES ]; do 10567754Smsmith if [ "$USE_XDIALOG" ]; then 10667754Smsmith password=$( $DIALOG \ 10767754Smsmith --title "$DIALOG_TITLE" \ 10867754Smsmith --backtitle "$DIALOG_BACKTITLE" \ 10967754Smsmith --hline "$hline" \ 11067754Smsmith --ok-label "$msg_ok" \ 11167754Smsmith --cancel-label "$msg_cancel" \ 11267754Smsmith --password --inputbox "$msg" $size \ 11367754Smsmith 2>&1 > /dev/null ) 11467754Smsmith retval=$? 11567754Smsmith 11667754Smsmith # Catch X11-related errors 11777424Smsmith [ $retval -eq 255 ] && 11867754Smsmith f_die $retval "$password" 11967754Smsmith else 12067754Smsmith $DIALOG \ 12167754Smsmith --title "$DIALOG_TITLE" \ 12267754Smsmith --backtitle "$DIALOG_BACKTITLE" \ 12367754Smsmith --hline "$hline" \ 12469746Smsmith --ok-label "$msg_ok" \ 12567754Smsmith --cancel-label "$msg_cancel" \ 12667754Smsmith --insecure \ 12777424Smsmith --passwordbox "$msg" $size \ 12877424Smsmith 2> "$DIALOG_TMPDIR/dialog.inputbox.$$" 12967754Smsmith retval=$? 13067754Smsmith password=$( f_dialog_inputstr ) 13180062Smsmith fi 13267754Smsmith 13367754Smsmith # Exit if the user cancelled. 13480062Smsmith [ $retval -eq $SUCCESS ] || exit $retval 13580062Smsmith 13680062Smsmith # 13780062Smsmith # Validate sudo(8) credentials 13880062Smsmith # 13980062Smsmith sudo -S -v 2> /dev/null <<-EOF 14080062Smsmith $password 14180062Smsmith EOF 14280062Smsmith retval=$? 14380062Smsmith unset password # scrub memory 14480062Smsmith if [ $retval -eq $SUCCESS ]; then 14580062Smsmith # Access granted... 14680062Smsmith break 14780062Smsmith else 14880062Smsmith # Access denied... 14980062Smsmith nfailures=$(( $nfailures + 1 )) 15080062Smsmith 15180062Smsmith # introduce a short delay 15280062Smsmith if [ $nfailures -lt $PASSWD_TRIES ]; then 15380062Smsmith f_dialog_info "$msg_sorry_try_again" 15480062Smsmith sleep 1 15580062Smsmith fi 15680062Smsmith fi 15780062Smsmith done 15880062Smsmith 15980062Smsmith # 16080062Smsmith # If user exhausted number of allowed password tries, log 16180062Smsmith # the security event and exit immediately. 16280062Smsmith # 16380062Smsmith if [ $nfailures -ge $PASSWD_TRIES ]; then 16480062Smsmith msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 16580062Smsmith logger -p auth.notice -t sudo " " \ 16680062Smsmith "$USER : $msg" \ 16780062Smsmith "; TTY=$(tty)" \ 16880062Smsmith "; PWD=$PWD" \ 16980062Smsmith "; USER=root" \ 17080062Smsmith "; COMMAND=$0" 17180062Smsmith f_die 1 "sudo: $msg" 17280062Smsmith fi 17380062Smsmith fi 17480062Smsmith 17580062Smsmith # Use xauth(1) to grant root the ability to use this X11/SSH session 17680062Smsmith if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 17780062Smsmith f_have xauth || f_die 1 \ 17880062Smsmith "$msg_no_such_file_or_directory" "$pgm" "xauth" 17980062Smsmith local HOSTNAME displaynum 18080062Smsmith HOSTNAME=$(hostname) 18180062Smsmith displaynum="${DISPLAY#*:}" 18280062Smsmith xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 18380062Smsmith $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 18480062Smsmith ~root/.Xauthority merge - > /dev/null 2>&1' 18580062Smsmith fi 18680062Smsmith 18780062Smsmith # Re-execute ourselves with sudo(8) 18880062Smsmith if [ $ARGC -gt 0 ]; then 18980062Smsmith exec sudo "$0" $ARGV 19080062Smsmith else 19180062Smsmith exec sudo "$0" 19280062Smsmith fi 19380062Smsmith exit $? # Never reached unless error 19480062Smsmith} 19580062Smsmith 19680062Smsmith# f_authenticate_some_user 19780062Smsmith# 19880062Smsmith# Only used if running as root and requires X11 (see USE_XDIALOG below). 19980062Smsmith# Prompts the user to enter a username and password to be authenticated via 20080062Smsmith# sudo(8) to proceed. 20180062Smsmith# 20280062Smsmith# The following environment variables effect functionality: 20380062Smsmith# 20480062Smsmith# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 20580062Smsmith# that Xdialog(1) should be used instead of dialog(1). 20680062Smsmith# 20780062Smsmithf_authenticate_some_user() 20867754Smsmith{ 20967754Smsmith local msg hline size width height 21067754Smsmith 21167754Smsmith f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 21267754Smsmith 21367754Smsmith # 21467754Smsmith # Secure-mode has been requested. 21567754Smsmith # 21667754Smsmith 21767754Smsmith [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 21867754Smsmith [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 21967754Smsmith 22067754Smsmith # 22167754Smsmith # Prompt for sudo(8) credentials. 22267754Smsmith # 22367754Smsmith 22467754Smsmith msg="$msg_please_enter_username_password" 22567754Smsmith hline="$hline_alnum_punc_tab_enter" 22667754Smsmith size=$( f_xdialog_2inputsbox_size \ 22777424Smsmith "$DIALOG_TITLE" \ 22867754Smsmith "$DIALOG_BACKTITLE" \ 22967754Smsmith "$msg" \ 23067754Smsmith "$field_username" "" \ 23167754Smsmith "$field_password" "" ) 23267754Smsmith width="${size##*[$IFS]}" 23367754Smsmith height="${size%%[$IFS]*}" 23467754Smsmith height=$(( $height + 2 )) # Add height for --password 23567754Smsmith 23667754Smsmith # 23767754Smsmith # Continue prompting until they either Cancel, succeed or exceed the 23867754Smsmith # number of allowed failures. 23967754Smsmith # 24067754Smsmith local user_pass nfailures=0 retval 24167754Smsmith while [ $nfailures -lt $PASSWD_TRIES ]; do 24267754Smsmith user_pass=$( $DIALOG \ 24367754Smsmith --title "$DIALOG_TITLE" \ 24467754Smsmith --backtitle "$DIALOG_BACKTITLE" \ 24569450Smsmith --hline "$hline" \ 24669450Smsmith --ok-label "$msg_ok" \ 24769450Smsmith --cancel-label "$msg_cancel" \ 24867754Smsmith --password --2inputsbox "$msg" \ 24967754Smsmith $height $width \ 25067754Smsmith "$field_username" "" \ 25167754Smsmith "$field_password" "" \ 25267754Smsmith 2>&1 > /dev/null ) 25367754Smsmith retval=$? 25467754Smsmith 25567754Smsmith # Catch X11-related errors 25667754Smsmith [ $retval -eq 255 ] && f_die $retval "$user_pass" 25767754Smsmith 25867754Smsmith # Exit if the user cancelled. 25967754Smsmith [ $retval -eq $SUCCESS ] || exit $retval 26077424Smsmith 26177424Smsmith # 26277424Smsmith # Make sure the user exists and is non-root 26377424Smsmith # 26467754Smsmith local user password 26567754Smsmith user="${user_pass%%/*}" 26667754Smsmith password="${user_pass#*/}" 26767754Smsmith unset user_pass # scrub memory 26867754Smsmith if [ ! "$user" ]; then 26967754Smsmith nfailures=$(( $nfailures + 1 )) 27077424Smsmith f_dialog_msgbox "$msg_no_username" 27167754Smsmith continue 27267754Smsmith fi 27371867Smsmith if [ ! "$SECURE_ALLOW_ROOT" ]; then 27467754Smsmith case "$user" in 27567754Smsmith root|toor) 27677424Smsmith nfailures=$(( $nfailures + 1 )) 27767754Smsmith f_dialog_msgbox "$( printf \ 27867754Smsmith "$msg_user_disallowed" "$user" )" 27967754Smsmith continue 28067754Smsmith esac 28167754Smsmith fi 28267754Smsmith if ! f_quietly id "$user"; then 28367754Smsmith nfailures=$(( $nfailures + 1 )) 28467754Smsmith if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 28567754Smsmith f_dialog_msgbox "$( printf \ 28667754Smsmith "$msg_unknown_user" "$user" )" 28767754Smsmith elif [ $nfailures -lt $PASSWD_TRIES ]; then 28867754Smsmith f_dialog_info "$msg_sorry_try_again" 28967754Smsmith sleep 1 29067754Smsmith fi 29167754Smsmith continue 29267754Smsmith fi 29367754Smsmith 29467754Smsmith # 29567754Smsmith # Validate sudo(8) credentials for given user 29667754Smsmith # 29767754Smsmith su -m "$user" <<-EOF 29867754Smsmith sh <<EOS 29967754Smsmith sudo -k 30067754Smsmith sudo -S -v 2> /dev/null <<EOP 30167754Smsmith $password 30267754Smsmith EOP 30367754Smsmith EOS 30467754Smsmith EOF 30567754Smsmith retval=$? 30667754Smsmith unset user 30767754Smsmith unset password # scrub memory 30867754Smsmith 30967754Smsmith if [ $retval -eq $SUCCESS ]; then 31067754Smsmith # Access granted... 31167754Smsmith break 31267754Smsmith else 31378986Smsmith # Access denied... 31478986Smsmith nfailures=$(( $nfailures + 1 )) 31578986Smsmith 31678986Smsmith # introduce a short delay 31778986Smsmith if [ $nfailures -lt $PASSWD_TRIES ]; then 31878986Smsmith f_dialog_info "$msg_sorry_try_again" 31978986Smsmith sleep 1 32078986Smsmith fi 32178986Smsmith fi 32278986Smsmith done 32378986Smsmith 32478986Smsmith # 32567754Smsmith # If user exhausted number of allowed password tries, log 32667754Smsmith # the security event and exit immediately. 32767754Smsmith # 32871867Smsmith if [ $nfailures -ge $PASSWD_TRIES ]; then 32971867Smsmith msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 33071867Smsmith logger -p auth.notice -t sudo " " \ 33171867Smsmith "${SUDO_USER:-$USER} : $msg" \ 33271867Smsmith "; TTY=$(tty)" \ 33371867Smsmith "; PWD=$PWD" \ 33482367Smsmith "; USER=root" \ 33582367Smsmith "; COMMAND=$0" 33682367Smsmith f_die 1 "sudo: $message" 33782367Smsmith fi 33882367Smsmith} 33971867Smsmith 34082367Smsmith# f_mustberoot_init 34182367Smsmith# 34282367Smsmith# If not already root, make the switch to root by re-executing ourselves via 34367754Smsmith# sudo(8) using user-supplied credentials. 34467754Smsmith# 34567754Smsmith# The following environment variables effect functionality: 34667754Smsmith# 34769450Smsmith# SECURE Either NULL or Non-NULL. If given a value will indicate 34869450Smsmith# that (while running as root) sudo(8) authentication is 34969450Smsmith# required to proceed. 35069450Smsmith# 35167754Smsmithf_mustberoot_init() 35267754Smsmith{ 35367754Smsmith if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 35467754Smsmith f_become_root_via_sudo 35567754Smsmith elif [ "$SECURE" ]; then 35667754Smsmith f_authenticate_some_user 35767754Smsmith fi 35867754Smsmith} 35969450Smsmith 36067754Smsmithfi # ! $_MUSTBEROOT_SUBR 36169450Smsmith