mustberoot.subr revision 252995
1252995Sdteskeif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 2252995Sdteske# 3252995Sdteske# Copyright (c) 2006-2013 Devin Teske 4252995Sdteske# All rights reserved. 5252995Sdteske# 6252995Sdteske# Redistribution and use in source and binary forms, with or without 7252995Sdteske# modification, are permitted provided that the following conditions 8252995Sdteske# are met: 9252995Sdteske# 1. Redistributions of source code must retain the above copyright 10252995Sdteske# notice, this list of conditions and the following disclaimer. 11252995Sdteske# 2. Redistributions in binary form must reproduce the above copyright 12252995Sdteske# notice, this list of conditions and the following disclaimer in the 13252995Sdteske# documentation and/or other materials provided with the distribution. 14252995Sdteske# 15252995Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16252995Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17252995Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18252995Sdteske# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19252995Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20252995Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21252995Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22252995Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23252995Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24252995Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25252995Sdteske# SUCH DAMAGE. 26252995Sdteske# 27252995Sdteske# $FreeBSD: stable/9/usr.sbin/bsdconfig/share/mustberoot.subr 252995 2013-07-07 19:13:34Z dteske $ 28252995Sdteske# 29252995Sdteske############################################################ INCLUDES 30252995Sdteske 31252995SdteskeBSDCFG_SHARE="/usr/share/bsdconfig" 32252995Sdteske. $BSDCFG_SHARE/common.subr || exit 1 33252995Sdteskef_dprintf "%s: loading includes..." mustberoot.subr 34252995Sdteskef_include $BSDCFG_SHARE/dialog.subr 35252995Sdteske 36252995SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig" 37252995Sdteskef_include_lang $BSDCFG_LIBE/include/messages.subr 38252995Sdteske 39252995Sdteske############################################################ CONFIGURATION 40252995Sdteske# NOTE: These are not able to be overridden/inherited for security purposes. 41252995Sdteske 42252995Sdteske# 43252995Sdteske# Number of tries a user gets to enter his/her password before we log the 44252995Sdteske# sudo(8) failure and exit. 45252995Sdteske# 46252995SdteskePASSWD_TRIES=3 47252995Sdteske 48252995Sdteske# 49252995Sdteske# While in SECURE mode, should authentication as `root' be allowed? Set to 50252995Sdteske# non-NULL to enable authentication as `root', otherwise disabled. 51252995Sdteske# 52252995Sdteske# WARNING: 53252995Sdteske# Unless using a custom sudo(8) configuration, user `root' should not be 54252995Sdteske# allowed because no password is required to become `root' when already `root' 55252995Sdteske# and therefore, any value entered as password will work. 56252995Sdteske# 57252995SdteskeSECURE_ALLOW_ROOT= 58252995Sdteske 59252995Sdteske# 60252995Sdteske# While in SECURE mode, should we divulge (through error message) when the 61252995Sdteske# requested authentication user does not exist? Set to non-NULL to enable, 62252995Sdteske# otherwise a non-existent user is treated like an invalid password. 63252995Sdteske# 64252995SdteskeSECURE_DIVULGE_UNKNOWN_USER= 65252995Sdteske 66252995Sdteske############################################################ FUNCTIONS 67252995Sdteske 68252995Sdteske# f_become_root_via_sudo 69252995Sdteske# 70252995Sdteske# If not running as root, prompt for sudo(8) credentials to become root. 71252995Sdteske# Re-execution of the current program via sudo is automatically handled. 72252995Sdteske# 73252995Sdteske# The following environment variables effect functionality: 74252995Sdteske# 75252995Sdteske# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 76252995Sdteske# that Xdialog(1) should be used instead of dialog(1). 77252995Sdteske# 78252995Sdteskef_become_root_via_sudo() 79252995Sdteske{ 80252995Sdteske local prompt hline height width rows msg 81252995Sdteske 82252995Sdteske [ "$( id -u )" = "0" ] && return $SUCCESS 83252995Sdteske 84252995Sdteske f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 85252995Sdteske 86252995Sdteske # 87252995Sdteske # Ask the user if it's OK to become root via sudo(8) and give them 88252995Sdteske # the option to save this preference (by touch(1)ing a file in the 89252995Sdteske # user's $HOME directory). 90252995Sdteske # 91252995Sdteske local checkpath="${HOME%/}/.bsdconfig_uses_sudo" 92252995Sdteske if [ ! -e "$checkpath" ]; then 93252995Sdteske prompt=$( printf "$msg_you_are_not_root_but" bsdconfig ) 94252995Sdteske msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" ) 95252995Sdteske local menu_list=" 96252995Sdteske 'X' '$msg_cancel_exit' 97252995Sdteske '1' '$msg' 98252995Sdteske '2' '$msg_try_sudo_only_this_once' 99252995Sdteske " # END-QUOTE 100252995Sdteske hline="$hline_arrows_tab_enter" 101252995Sdteske 102252995Sdteske eval f_dialog_menu_size height width rows \ 103252995Sdteske \"\$DIALOG_TITLE\" \ 104252995Sdteske \"\$DIALOG_BACKTITLE\" \ 105252995Sdteske \"\$prompt\" \ 106252995Sdteske \"\$hline\" \ 107252995Sdteske $menu_list 108252995Sdteske 109252995Sdteske local mtag 110252995Sdteske mtag=$( eval $DIALOG \ 111252995Sdteske --title \"\$DIALOG_TITLE\" \ 112252995Sdteske --backtitle \"\$DIALOG_BACKTITLE\" \ 113252995Sdteske --hline \"\$hline\" \ 114252995Sdteske --ok-label \"\$msg_ok\" \ 115252995Sdteske --cancel-label \"\$msg_cancel\" \ 116252995Sdteske --menu \"\$prompt\" \ 117252995Sdteske $height $width $rows \ 118252995Sdteske $menu_list \ 119252995Sdteske 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 120252995Sdteske ) || f_die 121252995Sdteske f_dialog_data_sanitize mtag 122252995Sdteske 123252995Sdteske case "$mtag" in 124252995Sdteske X) # Cancel/Exit 125252995Sdteske f_die ;; 126252995Sdteske 1) # Always try sudo(8) when run as $user 127252995Sdteske local err 128252995Sdteske if ! err=$( touch "$checkpath" 2>&1 ); then 129252995Sdteske f_dialog_msgbox "$err" 130252995Sdteske else 131252995Sdteske f_show_msg "$msg_created_path" "$checkpath" 132252995Sdteske fi 133252995Sdteske esac 134252995Sdteske else 135252995Sdteske # 136252995Sdteske # This user has created the path signing-off on sudo(8)-use 137252995Sdteske # but let's still give them a short/quick/unobtrusive reminder 138252995Sdteske # 139252995Sdteske f_dialog_info "$msg_becoming_root_via_sudo" 140252995Sdteske [ "$USE_XDIALOG" ] || sleep 0.6 141252995Sdteske fi 142252995Sdteske 143252995Sdteske # 144252995Sdteske # Check sudo(8) access before prompting for password. 145252995Sdteske # 146252995Sdteske :| sudo -S -v 2> /dev/null 147252995Sdteske if [ $? -ne $SUCCESS ]; then 148252995Sdteske # 149252995Sdteske # sudo(8) access denied. Prompt for their password. 150252995Sdteske # 151252995Sdteske prompt="$msg_please_enter_password" 152252995Sdteske hline="$hline_alnum_punc_tab_enter" 153252995Sdteske f_dialog_inputbox_size height width \ 154252995Sdteske "$DIALOG_TITLE" \ 155252995Sdteske "$DIALOG_BACKTITLE" \ 156252995Sdteske "$prompt" \ 157252995Sdteske "$hline" 158252995Sdteske 159252995Sdteske # 160252995Sdteske # Continue prompting until they either Cancel, succeed 161252995Sdteske # or exceed the number of allowed failures. 162252995Sdteske # 163252995Sdteske local password nfailures=0 retval 164252995Sdteske while [ $nfailures -lt $PASSWD_TRIES ]; do 165252995Sdteske if [ "$USE_XDIALOG" ]; then 166252995Sdteske password=$( $DIALOG \ 167252995Sdteske --title "$DIALOG_TITLE" \ 168252995Sdteske --backtitle "$DIALOG_BACKTITLE" \ 169252995Sdteske --hline "$hline" \ 170252995Sdteske --ok-label "$msg_ok" \ 171252995Sdteske --cancel-label "$msg_cancel" \ 172252995Sdteske --password --inputbox "$prompt" \ 173252995Sdteske $height $width \ 174252995Sdteske 2>&1 > /dev/null 175252995Sdteske ) 176252995Sdteske retval=$? 177252995Sdteske 178252995Sdteske # Catch X11-related errors 179252995Sdteske if [ $retval -eq 255 ]; then 180252995Sdteske f_die $retval "$password" 181252995Sdteske elif [ $retval -ne 0 ]; then 182252995Sdteske # User cancelled 183252995Sdteske exit $retval 184252995Sdteske fi 185252995Sdteske else 186252995Sdteske password=$( $DIALOG \ 187252995Sdteske --title "$DIALOG_TITLE" \ 188252995Sdteske --backtitle "$DIALOG_BACKTITLE" \ 189252995Sdteske --hline "$hline" \ 190252995Sdteske --ok-label "$msg_ok" \ 191252995Sdteske --cancel-label "$msg_cancel" \ 192252995Sdteske --insecure \ 193252995Sdteske --passwordbox "$prompt" \ 194252995Sdteske $height $width \ 195252995Sdteske 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 196252995Sdteske ) || exit $? 197252995Sdteske fi 198252995Sdteske debug= f_dialog_line_sanitize password 199252995Sdteske 200252995Sdteske # 201252995Sdteske # Validate sudo(8) credentials 202252995Sdteske # 203252995Sdteske sudo -S -v 2> /dev/null <<-EOF 204252995Sdteske $password 205252995Sdteske EOF 206252995Sdteske retval=$? 207252995Sdteske unset password # scrub memory 208252995Sdteske if [ $retval -eq $SUCCESS ]; then 209252995Sdteske # Access granted... 210252995Sdteske break 211252995Sdteske else 212252995Sdteske # Access denied... 213252995Sdteske nfailures=$(( $nfailures + 1 )) 214252995Sdteske 215252995Sdteske # introduce a short delay 216252995Sdteske if [ $nfailures -lt $PASSWD_TRIES ]; then 217252995Sdteske f_dialog_info "$msg_sorry_try_again" 218252995Sdteske sleep 1 219252995Sdteske fi 220252995Sdteske fi 221252995Sdteske done 222252995Sdteske 223252995Sdteske # 224252995Sdteske # If user exhausted number of allowed password tries, log 225252995Sdteske # the security event and exit immediately. 226252995Sdteske # 227252995Sdteske if [ $nfailures -ge $PASSWD_TRIES ]; then 228252995Sdteske msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 229252995Sdteske logger -p auth.notice -t sudo " " \ 230252995Sdteske "$USER : $msg" \ 231252995Sdteske "; TTY=$(tty)" \ 232252995Sdteske "; PWD=$PWD" \ 233252995Sdteske "; USER=root" \ 234252995Sdteske "; COMMAND=$0" 235252995Sdteske f_die 1 "sudo: $msg" 236252995Sdteske fi 237252995Sdteske fi 238252995Sdteske 239252995Sdteske # Use xauth(1) to grant root the ability to use this X11/SSH session 240252995Sdteske if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 241252995Sdteske f_have xauth || f_die 1 \ 242252995Sdteske "$msg_no_such_file_or_directory" "$pgm" "xauth" 243252995Sdteske local HOSTNAME displaynum 244252995Sdteske HOSTNAME=$(hostname) 245252995Sdteske displaynum="${DISPLAY#*:}" 246252995Sdteske xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 247252995Sdteske $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 248252995Sdteske ~root/.Xauthority merge - > /dev/null 2>&1' 249252995Sdteske fi 250252995Sdteske 251252995Sdteske # Re-execute ourselves with sudo(8) 252252995Sdteske f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr 253252995Sdteske if [ $ARGC -gt 0 ]; then 254252995Sdteske exec sudo "$0" $ARGV 255252995Sdteske else 256252995Sdteske exec sudo "$0" 257252995Sdteske fi 258252995Sdteske exit $? # Never reached unless error 259252995Sdteske} 260252995Sdteske 261252995Sdteske# f_authenticate_some_user 262252995Sdteske# 263252995Sdteske# Only used if running as root and requires X11 (see USE_XDIALOG below). 264252995Sdteske# Prompts the user to enter a username and password to be authenticated via 265252995Sdteske# sudo(8) to proceed. 266252995Sdteske# 267252995Sdteske# The following environment variables effect functionality: 268252995Sdteske# 269252995Sdteske# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 270252995Sdteske# that Xdialog(1) should be used instead of dialog(1). 271252995Sdteske# 272252995Sdteskef_authenticate_some_user() 273252995Sdteske{ 274252995Sdteske local msg hline height width 275252995Sdteske 276252995Sdteske f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 277252995Sdteske 278252995Sdteske # 279252995Sdteske # Secure-mode has been requested. 280252995Sdteske # 281252995Sdteske 282252995Sdteske [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 283252995Sdteske [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 284252995Sdteske 285252995Sdteske # 286252995Sdteske # Prompt for sudo(8) credentials. 287252995Sdteske # 288252995Sdteske 289252995Sdteske msg="$msg_please_enter_username_password" 290252995Sdteske hline="$hline_alnum_punc_tab_enter" 291252995Sdteske f_xdialog_2inputsbox_size height width \ 292252995Sdteske "$DIALOG_TITLE" \ 293252995Sdteske "$DIALOG_BACKTITLE" \ 294252995Sdteske "$msg" \ 295252995Sdteske "$field_username" "" \ 296252995Sdteske "$field_password" "" 297252995Sdteske height=$(( $height + 2 )) # Add height for --password 298252995Sdteske 299252995Sdteske # 300252995Sdteske # Continue prompting until they either Cancel, succeed or exceed the 301252995Sdteske # number of allowed failures. 302252995Sdteske # 303252995Sdteske local user_pass nfailures=0 retval 304252995Sdteske while [ $nfailures -lt $PASSWD_TRIES ]; do 305252995Sdteske user_pass=$( $DIALOG \ 306252995Sdteske --title "$DIALOG_TITLE" \ 307252995Sdteske --backtitle "$DIALOG_BACKTITLE" \ 308252995Sdteske --hline "$hline" \ 309252995Sdteske --ok-label "$msg_ok" \ 310252995Sdteske --cancel-label "$msg_cancel" \ 311252995Sdteske --password --2inputsbox "$msg" \ 312252995Sdteske $height $width \ 313252995Sdteske "$field_username" "" \ 314252995Sdteske "$field_password" "" \ 315252995Sdteske 2>&1 > /dev/null ) 316252995Sdteske retval=$? 317252995Sdteske 318252995Sdteske # Catch X11-related errors 319252995Sdteske [ $retval -eq 255 ] && f_die $retval "$user_pass" 320252995Sdteske 321252995Sdteske # Exit if the user cancelled. 322252995Sdteske [ $retval -eq $SUCCESS ] || exit $retval 323252995Sdteske 324252995Sdteske # 325252995Sdteske # Make sure the user exists and is non-root 326252995Sdteske # 327252995Sdteske local user password 328252995Sdteske user="${user_pass%%/*}" 329252995Sdteske password="${user_pass#*/}" 330252995Sdteske unset user_pass # scrub memory 331252995Sdteske if [ ! "$user" ]; then 332252995Sdteske nfailures=$(( $nfailures + 1 )) 333252995Sdteske f_show_msg "$msg_no_username" 334252995Sdteske continue 335252995Sdteske fi 336252995Sdteske if [ ! "$SECURE_ALLOW_ROOT" ]; then 337252995Sdteske case "$user" in 338252995Sdteske root|toor) 339252995Sdteske nfailures=$(( $nfailures + 1 )) 340252995Sdteske f_show_msg "$msg_user_disallowed" "$user" 341252995Sdteske continue 342252995Sdteske esac 343252995Sdteske fi 344252995Sdteske if ! f_quietly id "$user"; then 345252995Sdteske nfailures=$(( $nfailures + 1 )) 346252995Sdteske if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 347252995Sdteske f_show_msg "$msg_unknown_user" "$user" 348252995Sdteske elif [ $nfailures -lt $PASSWD_TRIES ]; then 349252995Sdteske f_dialog_info "$msg_sorry_try_again" 350252995Sdteske sleep 1 351252995Sdteske fi 352252995Sdteske continue 353252995Sdteske fi 354252995Sdteske 355252995Sdteske # 356252995Sdteske # Validate sudo(8) credentials for given user 357252995Sdteske # 358252995Sdteske su -m "$user" <<-EOF 359252995Sdteske sh <<EOS 360252995Sdteske sudo -k 361252995Sdteske sudo -S -v 2> /dev/null <<EOP 362252995Sdteske $password 363252995Sdteske EOP 364252995Sdteske EOS 365252995Sdteske EOF 366252995Sdteske retval=$? 367252995Sdteske unset user 368252995Sdteske unset password # scrub memory 369252995Sdteske 370252995Sdteske if [ $retval -eq $SUCCESS ]; then 371252995Sdteske # Access granted... 372252995Sdteske break 373252995Sdteske else 374252995Sdteske # Access denied... 375252995Sdteske nfailures=$(( $nfailures + 1 )) 376252995Sdteske 377252995Sdteske # introduce a short delay 378252995Sdteske if [ $nfailures -lt $PASSWD_TRIES ]; then 379252995Sdteske f_dialog_info "$msg_sorry_try_again" 380252995Sdteske sleep 1 381252995Sdteske fi 382252995Sdteske fi 383252995Sdteske done 384252995Sdteske 385252995Sdteske # 386252995Sdteske # If user exhausted number of allowed password tries, log 387252995Sdteske # the security event and exit immediately. 388252995Sdteske # 389252995Sdteske if [ $nfailures -ge $PASSWD_TRIES ]; then 390252995Sdteske msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 391252995Sdteske logger -p auth.notice -t sudo " " \ 392252995Sdteske "${SUDO_USER:-$USER} : $msg" \ 393252995Sdteske "; TTY=$(tty)" \ 394252995Sdteske "; PWD=$PWD" \ 395252995Sdteske "; USER=root" \ 396252995Sdteske "; COMMAND=$0" 397252995Sdteske f_die 1 "sudo: $message" 398252995Sdteske fi 399252995Sdteske} 400252995Sdteske 401252995Sdteske# f_mustberoot_init 402252995Sdteske# 403252995Sdteske# If not already root, make the switch to root by re-executing ourselves via 404252995Sdteske# sudo(8) using user-supplied credentials. 405252995Sdteske# 406252995Sdteske# The following environment variables effect functionality: 407252995Sdteske# 408252995Sdteske# SECURE Either NULL or Non-NULL. If given a value will indicate 409252995Sdteske# that (while running as root) sudo(8) authentication is 410252995Sdteske# required to proceed. 411252995Sdteske# 412252995Sdteskef_mustberoot_init() 413252995Sdteske{ 414252995Sdteske if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 415252995Sdteske f_become_root_via_sudo 416252995Sdteske elif [ "$SECURE" ]; then 417252995Sdteske f_authenticate_some_user 418252995Sdteske fi 419252995Sdteske} 420252995Sdteske 421252995Sdteske############################################################ MAIN 422252995Sdteske 423252995Sdteskef_dprintf "%s: Successfully loaded." mustberoot.subr 424252995Sdteske 425252995Sdteskefi # ! $_MUSTBEROOT_SUBR 426