mustberoot.subr revision 251190
1238438Sdteskeif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 2238438Sdteske# 3249746Sdteske# Copyright (c) 2006-2013 Devin Teske 4238438Sdteske# All Rights Reserved. 5238438Sdteske# 6238438Sdteske# Redistribution and use in source and binary forms, with or without 7238438Sdteske# modification, are permitted provided that the following conditions 8238438Sdteske# are met: 9238438Sdteske# 1. Redistributions of source code must retain the above copyright 10238438Sdteske# notice, this list of conditions and the following disclaimer. 11238438Sdteske# 2. Redistributions in binary form must reproduce the above copyright 12238438Sdteske# notice, this list of conditions and the following disclaimer in the 13238438Sdteske# documentation and/or other materials provided with the distribution. 14238438Sdteske# 15238438Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16238438Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE 17238438Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18238438Sdteske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19238438Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20238438Sdteske# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21238438Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22238438Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23238438Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24238438Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25238438Sdteske# SUCH DAMAGE. 26238438Sdteske# 27238438Sdteske# $FreeBSD: head/usr.sbin/bsdconfig/share/mustberoot.subr 251190 2013-05-31 19:07:17Z dteske $ 28238438Sdteske# 29238438Sdteske############################################################ INCLUDES 30238438Sdteske 31240684SdteskeBSDCFG_SHARE="/usr/share/bsdconfig" 32240684Sdteske. $BSDCFG_SHARE/common.subr || exit 1 33244675Sdteskef_dprintf "%s: loading includes..." mustberoot.subr 34240684Sdteskef_include $BSDCFG_SHARE/dialog.subr 35240684Sdteske 36238438SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig" 37238438Sdteskef_include_lang $BSDCFG_LIBE/include/messages.subr 38238438Sdteske 39238438Sdteske############################################################ CONFIGURATION 40238438Sdteske# NOTE: These are not able to be overridden/inherited for security purposes. 41238438Sdteske 42238438Sdteske# 43238438Sdteske# Number of tries a user gets to enter his/her password before we log the 44238438Sdteske# sudo(8) failure and exit. 45238438Sdteske# 46238438SdteskePASSWD_TRIES=3 47238438Sdteske 48238438Sdteske# 49238438Sdteske# While in SECURE mode, should authentication as `root' be allowed? Set to 50238438Sdteske# non-NULL to enable authentication as `root', otherwise disabled. 51238438Sdteske# 52238438Sdteske# WARNING: 53238438Sdteske# Unless using a custom sudo(8) configuration, user `root' should not be 54238438Sdteske# allowed because no password is required to become `root' when already `root' 55238438Sdteske# and therefore, any value entered as password will work. 56238438Sdteske# 57238438SdteskeSECURE_ALLOW_ROOT= 58238438Sdteske 59238438Sdteske# 60238438Sdteske# While in SECURE mode, should we divulge (through error message) when the 61238438Sdteske# requested authentication user does not exist? Set to non-NULL to enable, 62238438Sdteske# otherwise a non-existent user is treated like an invalid password. 63238438Sdteske# 64238438SdteskeSECURE_DIVULGE_UNKNOWN_USER= 65238438Sdteske 66238438Sdteske############################################################ FUNCTIONS 67238438Sdteske 68238438Sdteske# f_become_root_via_sudo 69238438Sdteske# 70238438Sdteske# If not running as root, prompt for sudo(8) credentials to become root. 71238438Sdteske# Re-execution of the current program via sudo is automatically handled. 72238438Sdteske# 73238438Sdteske# The following environment variables effect functionality: 74238438Sdteske# 75238438Sdteske# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 76238438Sdteske# that Xdialog(1) should be used instead of dialog(1). 77238438Sdteske# 78238438Sdteskef_become_root_via_sudo() 79238438Sdteske{ 80251190Sdteske local msg hline height width rows 81238438Sdteske 82238438Sdteske [ "$( id -u )" = "0" ] && return $SUCCESS 83238438Sdteske 84238438Sdteske f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 85238438Sdteske 86238438Sdteske # 87241653Sdteske # Ask the user if it's OK to become root via sudo(8) and give them 88241653Sdteske # the option to save this preference (by touch(1)ing a file in the 89241653Sdteske # user's $HOME directory). 90241653Sdteske # 91241653Sdteske local checkpath="${HOME%/}/.bsdconfig_uses_sudo" 92241653Sdteske if [ ! -e "$checkpath" ]; then 93241653Sdteske msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" ) 94241653Sdteske local menu_list=" 95241653Sdteske 'X' '$msg_cancel_exit' 96241653Sdteske '1' '$msg' 97241653Sdteske '2' '$msg_try_sudo_only_this_once' 98241653Sdteske " # END-QUOTE 99241653Sdteske msg=$( printf "$msg_you_are_not_root_but" bsdconfig ) 100241653Sdteske hline="$hline_arrows_tab_enter" 101251190Sdteske eval f_dialog_menu_size height width rows \ 102251190Sdteske \"\$DIALOG_TITLE\" \ 103251190Sdteske \"\$DIALOG_BACKTITLE\" \ 104251190Sdteske \"\$msg\" \ 105251190Sdteske \"\$hline\" \ 106251190Sdteske $menu_list 107241653Sdteske 108241653Sdteske local dialog_menu mtag retval 109241653Sdteske dialog_menu=$( eval $DIALOG \ 110241653Sdteske --title \"\$DIALOG_TITLE\" \ 111241653Sdteske --backtitle \"\$DIALOG_BACKTITLE\" \ 112241653Sdteske --hline \"\$hline\" \ 113241653Sdteske --ok-label \"\$msg_ok\" \ 114241653Sdteske --cancel-label \"\$msg_cancel\" \ 115251190Sdteske --menu \"\$msg\" \ 116251190Sdteske $height $width $rows \ 117241653Sdteske $menu_list \ 118241653Sdteske 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 119241653Sdteske ) 120241653Sdteske retval=$? 121241653Sdteske setvar DIALOG_MENU_$$ "$dialog_menu" 122241653Sdteske mtag=$( f_dialog_menutag ) 123241653Sdteske 124241653Sdteske [ $retval -eq 0 ] || f_die 125241653Sdteske 126241653Sdteske case "$mtag" in 127241653Sdteske X) # Cancel/Exit 128241653Sdteske f_die ;; 129241653Sdteske 1) # Always try sudo(8) when run as $user 130241653Sdteske local err 131241653Sdteske if ! err=$( touch "$checkpath" 2>&1 ); then 132245437Sdteske f_dialog_msgbox "$err" 133241653Sdteske else 134241653Sdteske f_show_msg "$msg_created_path" "$checkpath" 135241653Sdteske fi 136241653Sdteske esac 137241653Sdteske else 138241653Sdteske # 139241653Sdteske # This user has created the path signing-off on sudo(8)-use 140241653Sdteske # but let's still give them a short/quick/unobtrusive reminder 141241653Sdteske # 142241653Sdteske f_dialog_info "$msg_becoming_root_via_sudo" 143241653Sdteske [ "$USE_XDIALOG" ] || sleep 0.6 144241653Sdteske fi 145241653Sdteske 146241653Sdteske # 147238438Sdteske # Check sudo(8) access before prompting for password. 148238438Sdteske # 149240783Sdteske :| sudo -S -v 2> /dev/null 150238438Sdteske if [ $? -ne $SUCCESS ]; then 151238438Sdteske # 152238438Sdteske # sudo(8) access denied. Prompt for their password. 153238438Sdteske # 154238438Sdteske msg="$msg_please_enter_password" 155238438Sdteske hline="$hline_alnum_punc_tab_enter" 156251190Sdteske f_dialog_inputbox_size height width \ 157251190Sdteske "$DIALOG_TITLE" \ 158251190Sdteske "$DIALOG_BACKTITLE" \ 159251190Sdteske "$msg" \ 160251190Sdteske "$hline" 161238438Sdteske 162238438Sdteske # 163238438Sdteske # Continue prompting until they either Cancel, succeed 164238438Sdteske # or exceed the number of allowed failures. 165238438Sdteske # 166238438Sdteske local password nfailures=0 retval 167238438Sdteske while [ $nfailures -lt $PASSWD_TRIES ]; do 168238438Sdteske if [ "$USE_XDIALOG" ]; then 169238438Sdteske password=$( $DIALOG \ 170251190Sdteske --title "$DIALOG_TITLE" \ 171251190Sdteske --backtitle "$DIALOG_BACKTITLE" \ 172251190Sdteske --hline "$hline" \ 173251190Sdteske --ok-label "$msg_ok" \ 174251190Sdteske --cancel-label "$msg_cancel" \ 175251190Sdteske --password --inputbox "$msg" \ 176251190Sdteske $height $width \ 177240783Sdteske 2>&1 > /dev/null ) 178238438Sdteske retval=$? 179238438Sdteske 180238438Sdteske # Catch X11-related errors 181238438Sdteske [ $retval -eq 255 ] && 182238438Sdteske f_die $retval "$password" 183238438Sdteske else 184240768Sdteske local dialog_inputbox 185240768Sdteske dialog_inputbox=$( $DIALOG \ 186238438Sdteske --title "$DIALOG_TITLE" \ 187238438Sdteske --backtitle "$DIALOG_BACKTITLE" \ 188238438Sdteske --hline "$hline" \ 189238438Sdteske --ok-label "$msg_ok" \ 190238438Sdteske --cancel-label "$msg_cancel" \ 191238438Sdteske --insecure \ 192251190Sdteske --passwordbox "$msg" \ 193251190Sdteske $height $width \ 194240768Sdteske 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 195240768Sdteske ) 196238438Sdteske retval=$? 197240768Sdteske setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" 198238438Sdteske password=$( f_dialog_inputstr ) 199238438Sdteske fi 200238438Sdteske 201238438Sdteske # Exit if the user cancelled. 202238438Sdteske [ $retval -eq $SUCCESS ] || exit $retval 203238438Sdteske 204238438Sdteske # 205238438Sdteske # Validate sudo(8) credentials 206238438Sdteske # 207240783Sdteske sudo -S -v 2> /dev/null <<-EOF 208238438Sdteske $password 209238438Sdteske EOF 210238438Sdteske retval=$? 211238438Sdteske unset password # scrub memory 212238438Sdteske if [ $retval -eq $SUCCESS ]; then 213238438Sdteske # Access granted... 214238438Sdteske break 215238438Sdteske else 216238438Sdteske # Access denied... 217238438Sdteske nfailures=$(( $nfailures + 1 )) 218238438Sdteske 219238438Sdteske # introduce a short delay 220238438Sdteske if [ $nfailures -lt $PASSWD_TRIES ]; then 221238438Sdteske f_dialog_info "$msg_sorry_try_again" 222238438Sdteske sleep 1 223238438Sdteske fi 224238438Sdteske fi 225238438Sdteske done 226238438Sdteske 227238438Sdteske # 228238438Sdteske # If user exhausted number of allowed password tries, log 229238438Sdteske # the security event and exit immediately. 230238438Sdteske # 231238438Sdteske if [ $nfailures -ge $PASSWD_TRIES ]; then 232238438Sdteske msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 233238438Sdteske logger -p auth.notice -t sudo " " \ 234238438Sdteske "$USER : $msg" \ 235238438Sdteske "; TTY=$(tty)" \ 236238438Sdteske "; PWD=$PWD" \ 237238438Sdteske "; USER=root" \ 238238438Sdteske "; COMMAND=$0" 239238438Sdteske f_die 1 "sudo: $msg" 240238438Sdteske fi 241238438Sdteske fi 242238438Sdteske 243238438Sdteske # Use xauth(1) to grant root the ability to use this X11/SSH session 244238438Sdteske if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 245238438Sdteske f_have xauth || f_die 1 \ 246238438Sdteske "$msg_no_such_file_or_directory" "$pgm" "xauth" 247238438Sdteske local HOSTNAME displaynum 248238438Sdteske HOSTNAME=$(hostname) 249238438Sdteske displaynum="${DISPLAY#*:}" 250238438Sdteske xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 251238438Sdteske $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 252240783Sdteske ~root/.Xauthority merge - > /dev/null 2>&1' 253238438Sdteske fi 254238438Sdteske 255238438Sdteske # Re-execute ourselves with sudo(8) 256249746Sdteske f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr 257238438Sdteske if [ $ARGC -gt 0 ]; then 258238438Sdteske exec sudo "$0" $ARGV 259238438Sdteske else 260238438Sdteske exec sudo "$0" 261238438Sdteske fi 262238438Sdteske exit $? # Never reached unless error 263238438Sdteske} 264238438Sdteske 265238438Sdteske# f_authenticate_some_user 266238438Sdteske# 267238438Sdteske# Only used if running as root and requires X11 (see USE_XDIALOG below). 268238438Sdteske# Prompts the user to enter a username and password to be authenticated via 269238438Sdteske# sudo(8) to proceed. 270238438Sdteske# 271238438Sdteske# The following environment variables effect functionality: 272238438Sdteske# 273238438Sdteske# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 274238438Sdteske# that Xdialog(1) should be used instead of dialog(1). 275238438Sdteske# 276238438Sdteskef_authenticate_some_user() 277238438Sdteske{ 278251190Sdteske local msg hline height width 279238438Sdteske 280238438Sdteske f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 281238438Sdteske 282238438Sdteske # 283238438Sdteske # Secure-mode has been requested. 284238438Sdteske # 285243476Sdteske 286238438Sdteske [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 287238438Sdteske [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 288238438Sdteske 289238438Sdteske # 290238438Sdteske # Prompt for sudo(8) credentials. 291238438Sdteske # 292238438Sdteske 293238438Sdteske msg="$msg_please_enter_username_password" 294238438Sdteske hline="$hline_alnum_punc_tab_enter" 295251190Sdteske f_xdialog_2inputsbox_size height width \ 296251190Sdteske "$DIALOG_TITLE" \ 297251190Sdteske "$DIALOG_BACKTITLE" \ 298251190Sdteske "$msg" \ 299251190Sdteske "$field_username" "" \ 300251190Sdteske "$field_password" "" 301238438Sdteske height=$(( $height + 2 )) # Add height for --password 302238438Sdteske 303238438Sdteske # 304238438Sdteske # Continue prompting until they either Cancel, succeed or exceed the 305238438Sdteske # number of allowed failures. 306238438Sdteske # 307238438Sdteske local user_pass nfailures=0 retval 308238438Sdteske while [ $nfailures -lt $PASSWD_TRIES ]; do 309238438Sdteske user_pass=$( $DIALOG \ 310238438Sdteske --title "$DIALOG_TITLE" \ 311238438Sdteske --backtitle "$DIALOG_BACKTITLE" \ 312238438Sdteske --hline "$hline" \ 313238438Sdteske --ok-label "$msg_ok" \ 314238438Sdteske --cancel-label "$msg_cancel" \ 315238438Sdteske --password --2inputsbox "$msg" \ 316238438Sdteske $height $width \ 317238438Sdteske "$field_username" "" \ 318238438Sdteske "$field_password" "" \ 319240783Sdteske 2>&1 > /dev/null ) 320238438Sdteske retval=$? 321238438Sdteske 322238438Sdteske # Catch X11-related errors 323238438Sdteske [ $retval -eq 255 ] && f_die $retval "$user_pass" 324238438Sdteske 325238438Sdteske # Exit if the user cancelled. 326238438Sdteske [ $retval -eq $SUCCESS ] || exit $retval 327238438Sdteske 328238438Sdteske # 329238438Sdteske # Make sure the user exists and is non-root 330238438Sdteske # 331238438Sdteske local user password 332238438Sdteske user="${user_pass%%/*}" 333238438Sdteske password="${user_pass#*/}" 334238438Sdteske unset user_pass # scrub memory 335238438Sdteske if [ ! "$user" ]; then 336238438Sdteske nfailures=$(( $nfailures + 1 )) 337238438Sdteske f_dialog_msgbox "$msg_no_username" 338238438Sdteske continue 339238438Sdteske fi 340238438Sdteske if [ ! "$SECURE_ALLOW_ROOT" ]; then 341238438Sdteske case "$user" in 342238438Sdteske root|toor) 343238438Sdteske nfailures=$(( $nfailures + 1 )) 344244554Sdteske f_show_msg "$msg_user_disallowed" "$user" 345238438Sdteske continue 346238438Sdteske esac 347238438Sdteske fi 348238438Sdteske if ! f_quietly id "$user"; then 349238438Sdteske nfailures=$(( $nfailures + 1 )) 350238438Sdteske if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 351244554Sdteske f_show_msg "$msg_unknown_user" "$user" 352238438Sdteske elif [ $nfailures -lt $PASSWD_TRIES ]; then 353238438Sdteske f_dialog_info "$msg_sorry_try_again" 354238438Sdteske sleep 1 355238438Sdteske fi 356238438Sdteske continue 357238438Sdteske fi 358238438Sdteske 359238438Sdteske # 360238438Sdteske # Validate sudo(8) credentials for given user 361238438Sdteske # 362238438Sdteske su -m "$user" <<-EOF 363238438Sdteske sh <<EOS 364238438Sdteske sudo -k 365240783Sdteske sudo -S -v 2> /dev/null <<EOP 366238438Sdteske $password 367238438Sdteske EOP 368238438Sdteske EOS 369238438Sdteske EOF 370238438Sdteske retval=$? 371238438Sdteske unset user 372238438Sdteske unset password # scrub memory 373238438Sdteske 374238438Sdteske if [ $retval -eq $SUCCESS ]; then 375238438Sdteske # Access granted... 376238438Sdteske break 377238438Sdteske else 378238438Sdteske # Access denied... 379238438Sdteske nfailures=$(( $nfailures + 1 )) 380238438Sdteske 381238438Sdteske # introduce a short delay 382238438Sdteske if [ $nfailures -lt $PASSWD_TRIES ]; then 383238438Sdteske f_dialog_info "$msg_sorry_try_again" 384238438Sdteske sleep 1 385238438Sdteske fi 386238438Sdteske fi 387238438Sdteske done 388238438Sdteske 389238438Sdteske # 390238438Sdteske # If user exhausted number of allowed password tries, log 391238438Sdteske # the security event and exit immediately. 392238438Sdteske # 393238438Sdteske if [ $nfailures -ge $PASSWD_TRIES ]; then 394238438Sdteske msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 395238438Sdteske logger -p auth.notice -t sudo " " \ 396238438Sdteske "${SUDO_USER:-$USER} : $msg" \ 397238438Sdteske "; TTY=$(tty)" \ 398238438Sdteske "; PWD=$PWD" \ 399238438Sdteske "; USER=root" \ 400238438Sdteske "; COMMAND=$0" 401238438Sdteske f_die 1 "sudo: $message" 402238438Sdteske fi 403238438Sdteske} 404238438Sdteske 405238438Sdteske# f_mustberoot_init 406238438Sdteske# 407238438Sdteske# If not already root, make the switch to root by re-executing ourselves via 408238438Sdteske# sudo(8) using user-supplied credentials. 409238438Sdteske# 410238438Sdteske# The following environment variables effect functionality: 411238438Sdteske# 412238438Sdteske# SECURE Either NULL or Non-NULL. If given a value will indicate 413238438Sdteske# that (while running as root) sudo(8) authentication is 414238438Sdteske# required to proceed. 415238438Sdteske# 416238438Sdteskef_mustberoot_init() 417238438Sdteske{ 418238438Sdteske if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 419238438Sdteske f_become_root_via_sudo 420238438Sdteske elif [ "$SECURE" ]; then 421238438Sdteske f_authenticate_some_user 422238438Sdteske fi 423238438Sdteske} 424238438Sdteske 425244675Sdteske############################################################ MAIN 426244675Sdteske 427244675Sdteskef_dprintf "%s: Successfully loaded." mustberoot.subr 428244675Sdteske 429238438Sdteskefi # ! $_MUSTBEROOT_SUBR 430