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