mustberoot.subr revision 240783
1238438Sdteskeif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 2238438Sdteske# 3238438Sdteske# Copyright (c) 2006-2012 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 240783 2012-09-21 19:03:25Z dteske $ 28238438Sdteske# 29238438Sdteske############################################################ INCLUDES 30238438Sdteske 31240684SdteskeBSDCFG_SHARE="/usr/share/bsdconfig" 32240684Sdteske. $BSDCFG_SHARE/common.subr || exit 1 33240684Sdteskef_include $BSDCFG_SHARE/dialog.subr 34240684Sdteske 35238438SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig" 36238438Sdteskef_include_lang $BSDCFG_LIBE/include/messages.subr 37238438Sdteske 38238438Sdteske############################################################ CONFIGURATION 39238438Sdteske# NOTE: These are not able to be overridden/inherited for security purposes. 40238438Sdteske 41238438Sdteske# 42238438Sdteske# Number of tries a user gets to enter his/her password before we log the 43238438Sdteske# sudo(8) failure and exit. 44238438Sdteske# 45238438SdteskePASSWD_TRIES=3 46238438Sdteske 47238438Sdteske# 48238438Sdteske# While in SECURE mode, should authentication as `root' be allowed? Set to 49238438Sdteske# non-NULL to enable authentication as `root', otherwise disabled. 50238438Sdteske# 51238438Sdteske# WARNING: 52238438Sdteske# Unless using a custom sudo(8) configuration, user `root' should not be 53238438Sdteske# allowed because no password is required to become `root' when already `root' 54238438Sdteske# and therefore, any value entered as password will work. 55238438Sdteske# 56238438SdteskeSECURE_ALLOW_ROOT= 57238438Sdteske 58238438Sdteske# 59238438Sdteske# While in SECURE mode, should we divulge (through error message) when the 60238438Sdteske# requested authentication user does not exist? Set to non-NULL to enable, 61238438Sdteske# otherwise a non-existent user is treated like an invalid password. 62238438Sdteske# 63238438SdteskeSECURE_DIVULGE_UNKNOWN_USER= 64238438Sdteske 65238438Sdteske############################################################ FUNCTIONS 66238438Sdteske 67238438Sdteske# f_become_root_via_sudo 68238438Sdteske# 69238438Sdteske# If not running as root, prompt for sudo(8) credentials to become root. 70238438Sdteske# Re-execution of the current program via sudo is automatically handled. 71238438Sdteske# 72238438Sdteske# The following environment variables effect functionality: 73238438Sdteske# 74238438Sdteske# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 75238438Sdteske# that Xdialog(1) should be used instead of dialog(1). 76238438Sdteske# 77238438Sdteskef_become_root_via_sudo() 78238438Sdteske{ 79238438Sdteske local msg hline size 80238438Sdteske 81238438Sdteske [ "$( id -u )" = "0" ] && return $SUCCESS 82238438Sdteske 83238438Sdteske f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 84238438Sdteske 85238438Sdteske # 86238438Sdteske # Check sudo(8) access before prompting for password. 87238438Sdteske # 88240783Sdteske :| sudo -S -v 2> /dev/null 89238438Sdteske if [ $? -ne $SUCCESS ]; then 90238438Sdteske # 91238438Sdteske # sudo(8) access denied. Prompt for their password. 92238438Sdteske # 93238438Sdteske msg="$msg_please_enter_password" 94238438Sdteske hline="$hline_alnum_punc_tab_enter" 95238438Sdteske size=$( f_dialog_inputbox_size \ 96238438Sdteske "$DIALOG_TITLE" \ 97238438Sdteske "$DIALOG_BACKTITLE" \ 98238438Sdteske "$msg" \ 99238438Sdteske "$hline" ) 100238438Sdteske 101238438Sdteske # 102238438Sdteske # Continue prompting until they either Cancel, succeed 103238438Sdteske # or exceed the number of allowed failures. 104238438Sdteske # 105238438Sdteske local password nfailures=0 retval 106238438Sdteske while [ $nfailures -lt $PASSWD_TRIES ]; do 107238438Sdteske if [ "$USE_XDIALOG" ]; then 108238438Sdteske password=$( $DIALOG \ 109238438Sdteske --title "$DIALOG_TITLE" \ 110238438Sdteske --backtitle "$DIALOG_BACKTITLE" \ 111238438Sdteske --hline "$hline" \ 112238438Sdteske --ok-label "$msg_ok" \ 113238438Sdteske --cancel-label "$msg_cancel" \ 114238438Sdteske --password --inputbox "$msg" $size \ 115240783Sdteske 2>&1 > /dev/null ) 116238438Sdteske retval=$? 117238438Sdteske 118238438Sdteske # Catch X11-related errors 119238438Sdteske [ $retval -eq 255 ] && 120238438Sdteske f_die $retval "$password" 121238438Sdteske else 122240768Sdteske local dialog_inputbox 123240768Sdteske dialog_inputbox=$( $DIALOG \ 124238438Sdteske --title "$DIALOG_TITLE" \ 125238438Sdteske --backtitle "$DIALOG_BACKTITLE" \ 126238438Sdteske --hline "$hline" \ 127238438Sdteske --ok-label "$msg_ok" \ 128238438Sdteske --cancel-label "$msg_cancel" \ 129238438Sdteske --insecure \ 130238438Sdteske --passwordbox "$msg" $size \ 131240768Sdteske 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 132240768Sdteske ) 133238438Sdteske retval=$? 134240768Sdteske setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" 135238438Sdteske password=$( f_dialog_inputstr ) 136238438Sdteske fi 137238438Sdteske 138238438Sdteske # Exit if the user cancelled. 139238438Sdteske [ $retval -eq $SUCCESS ] || exit $retval 140238438Sdteske 141238438Sdteske # 142238438Sdteske # Validate sudo(8) credentials 143238438Sdteske # 144240783Sdteske sudo -S -v 2> /dev/null <<-EOF 145238438Sdteske $password 146238438Sdteske EOF 147238438Sdteske retval=$? 148238438Sdteske unset password # scrub memory 149238438Sdteske if [ $retval -eq $SUCCESS ]; then 150238438Sdteske # Access granted... 151238438Sdteske break 152238438Sdteske else 153238438Sdteske # Access denied... 154238438Sdteske nfailures=$(( $nfailures + 1 )) 155238438Sdteske 156238438Sdteske # introduce a short delay 157238438Sdteske if [ $nfailures -lt $PASSWD_TRIES ]; then 158238438Sdteske f_dialog_info "$msg_sorry_try_again" 159238438Sdteske sleep 1 160238438Sdteske fi 161238438Sdteske fi 162238438Sdteske done 163238438Sdteske 164238438Sdteske # 165238438Sdteske # If user exhausted number of allowed password tries, log 166238438Sdteske # the security event and exit immediately. 167238438Sdteske # 168238438Sdteske if [ $nfailures -ge $PASSWD_TRIES ]; then 169238438Sdteske msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 170238438Sdteske logger -p auth.notice -t sudo " " \ 171238438Sdteske "$USER : $msg" \ 172238438Sdteske "; TTY=$(tty)" \ 173238438Sdteske "; PWD=$PWD" \ 174238438Sdteske "; USER=root" \ 175238438Sdteske "; COMMAND=$0" 176238438Sdteske f_die 1 "sudo: $msg" 177238438Sdteske fi 178238438Sdteske fi 179238438Sdteske 180238438Sdteske # Use xauth(1) to grant root the ability to use this X11/SSH session 181238438Sdteske if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 182238438Sdteske f_have xauth || f_die 1 \ 183238438Sdteske "$msg_no_such_file_or_directory" "$pgm" "xauth" 184238438Sdteske local HOSTNAME displaynum 185238438Sdteske HOSTNAME=$(hostname) 186238438Sdteske displaynum="${DISPLAY#*:}" 187238438Sdteske xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 188238438Sdteske $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 189240783Sdteske ~root/.Xauthority merge - > /dev/null 2>&1' 190238438Sdteske fi 191238438Sdteske 192238438Sdteske # Re-execute ourselves with sudo(8) 193238438Sdteske if [ $ARGC -gt 0 ]; then 194238438Sdteske exec sudo "$0" $ARGV 195238438Sdteske else 196238438Sdteske exec sudo "$0" 197238438Sdteske fi 198238438Sdteske exit $? # Never reached unless error 199238438Sdteske} 200238438Sdteske 201238438Sdteske# f_authenticate_some_user 202238438Sdteske# 203238438Sdteske# Only used if running as root and requires X11 (see USE_XDIALOG below). 204238438Sdteske# Prompts the user to enter a username and password to be authenticated via 205238438Sdteske# sudo(8) to proceed. 206238438Sdteske# 207238438Sdteske# The following environment variables effect functionality: 208238438Sdteske# 209238438Sdteske# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 210238438Sdteske# that Xdialog(1) should be used instead of dialog(1). 211238438Sdteske# 212238438Sdteskef_authenticate_some_user() 213238438Sdteske{ 214238438Sdteske local msg hline size width height 215238438Sdteske 216238438Sdteske f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 217238438Sdteske 218238438Sdteske # 219238438Sdteske # Secure-mode has been requested. 220238438Sdteske # 221238438Sdteske 222238438Sdteske [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 223238438Sdteske [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 224238438Sdteske 225238438Sdteske # 226238438Sdteske # Prompt for sudo(8) credentials. 227238438Sdteske # 228238438Sdteske 229238438Sdteske msg="$msg_please_enter_username_password" 230238438Sdteske hline="$hline_alnum_punc_tab_enter" 231238438Sdteske size=$( f_xdialog_2inputsbox_size \ 232238438Sdteske "$DIALOG_TITLE" \ 233238438Sdteske "$DIALOG_BACKTITLE" \ 234238438Sdteske "$msg" \ 235238438Sdteske "$field_username" "" \ 236238438Sdteske "$field_password" "" ) 237238438Sdteske width="${size##*[$IFS]}" 238238438Sdteske height="${size%%[$IFS]*}" 239238438Sdteske height=$(( $height + 2 )) # Add height for --password 240238438Sdteske 241238438Sdteske # 242238438Sdteske # Continue prompting until they either Cancel, succeed or exceed the 243238438Sdteske # number of allowed failures. 244238438Sdteske # 245238438Sdteske local user_pass nfailures=0 retval 246238438Sdteske while [ $nfailures -lt $PASSWD_TRIES ]; do 247238438Sdteske user_pass=$( $DIALOG \ 248238438Sdteske --title "$DIALOG_TITLE" \ 249238438Sdteske --backtitle "$DIALOG_BACKTITLE" \ 250238438Sdteske --hline "$hline" \ 251238438Sdteske --ok-label "$msg_ok" \ 252238438Sdteske --cancel-label "$msg_cancel" \ 253238438Sdteske --password --2inputsbox "$msg" \ 254238438Sdteske $height $width \ 255238438Sdteske "$field_username" "" \ 256238438Sdteske "$field_password" "" \ 257240783Sdteske 2>&1 > /dev/null ) 258238438Sdteske retval=$? 259238438Sdteske 260238438Sdteske # Catch X11-related errors 261238438Sdteske [ $retval -eq 255 ] && f_die $retval "$user_pass" 262238438Sdteske 263238438Sdteske # Exit if the user cancelled. 264238438Sdteske [ $retval -eq $SUCCESS ] || exit $retval 265238438Sdteske 266238438Sdteske # 267238438Sdteske # Make sure the user exists and is non-root 268238438Sdteske # 269238438Sdteske local user password 270238438Sdteske user="${user_pass%%/*}" 271238438Sdteske password="${user_pass#*/}" 272238438Sdteske unset user_pass # scrub memory 273238438Sdteske if [ ! "$user" ]; then 274238438Sdteske nfailures=$(( $nfailures + 1 )) 275238438Sdteske f_dialog_msgbox "$msg_no_username" 276238438Sdteske continue 277238438Sdteske fi 278238438Sdteske if [ ! "$SECURE_ALLOW_ROOT" ]; then 279238438Sdteske case "$user" in 280238438Sdteske root|toor) 281238438Sdteske nfailures=$(( $nfailures + 1 )) 282238438Sdteske f_dialog_msgbox "$( printf \ 283238438Sdteske "$msg_user_disallowed" "$user" )" 284238438Sdteske continue 285238438Sdteske esac 286238438Sdteske fi 287238438Sdteske if ! f_quietly id "$user"; then 288238438Sdteske nfailures=$(( $nfailures + 1 )) 289238438Sdteske if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 290238438Sdteske f_dialog_msgbox "$( printf \ 291238438Sdteske "$msg_unknown_user" "$user" )" 292238438Sdteske elif [ $nfailures -lt $PASSWD_TRIES ]; then 293238438Sdteske f_dialog_info "$msg_sorry_try_again" 294238438Sdteske sleep 1 295238438Sdteske fi 296238438Sdteske continue 297238438Sdteske fi 298238438Sdteske 299238438Sdteske # 300238438Sdteske # Validate sudo(8) credentials for given user 301238438Sdteske # 302238438Sdteske su -m "$user" <<-EOF 303238438Sdteske sh <<EOS 304238438Sdteske sudo -k 305240783Sdteske sudo -S -v 2> /dev/null <<EOP 306238438Sdteske $password 307238438Sdteske EOP 308238438Sdteske EOS 309238438Sdteske EOF 310238438Sdteske retval=$? 311238438Sdteske unset user 312238438Sdteske unset password # scrub memory 313238438Sdteske 314238438Sdteske if [ $retval -eq $SUCCESS ]; then 315238438Sdteske # Access granted... 316238438Sdteske break 317238438Sdteske else 318238438Sdteske # Access denied... 319238438Sdteske nfailures=$(( $nfailures + 1 )) 320238438Sdteske 321238438Sdteske # introduce a short delay 322238438Sdteske if [ $nfailures -lt $PASSWD_TRIES ]; then 323238438Sdteske f_dialog_info "$msg_sorry_try_again" 324238438Sdteske sleep 1 325238438Sdteske fi 326238438Sdteske fi 327238438Sdteske done 328238438Sdteske 329238438Sdteske # 330238438Sdteske # If user exhausted number of allowed password tries, log 331238438Sdteske # the security event and exit immediately. 332238438Sdteske # 333238438Sdteske if [ $nfailures -ge $PASSWD_TRIES ]; then 334238438Sdteske msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 335238438Sdteske logger -p auth.notice -t sudo " " \ 336238438Sdteske "${SUDO_USER:-$USER} : $msg" \ 337238438Sdteske "; TTY=$(tty)" \ 338238438Sdteske "; PWD=$PWD" \ 339238438Sdteske "; USER=root" \ 340238438Sdteske "; COMMAND=$0" 341238438Sdteske f_die 1 "sudo: $message" 342238438Sdteske fi 343238438Sdteske} 344238438Sdteske 345238438Sdteske# f_mustberoot_init 346238438Sdteske# 347238438Sdteske# If not already root, make the switch to root by re-executing ourselves via 348238438Sdteske# sudo(8) using user-supplied credentials. 349238438Sdteske# 350238438Sdteske# The following environment variables effect functionality: 351238438Sdteske# 352238438Sdteske# SECURE Either NULL or Non-NULL. If given a value will indicate 353238438Sdteske# that (while running as root) sudo(8) authentication is 354238438Sdteske# required to proceed. 355238438Sdteske# 356238438Sdteskef_mustberoot_init() 357238438Sdteske{ 358238438Sdteske if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 359238438Sdteske f_become_root_via_sudo 360238438Sdteske elif [ "$SECURE" ]; then 361238438Sdteske f_authenticate_some_user 362238438Sdteske fi 363238438Sdteske} 364238438Sdteske 365238438Sdteskefi # ! $_MUSTBEROOT_SUBR 366