mustberoot.subr revision 244675
1if [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1 2# 3# Copyright (c) 2006-2012 Devin Teske 4# All Rights Reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE 17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25# SUCH DAMAGE. 26# 27# $FreeBSD: head/usr.sbin/bsdconfig/share/mustberoot.subr 244675 2012-12-25 10:47:45Z dteske $ 28# 29############################################################ INCLUDES 30 31BSDCFG_SHARE="/usr/share/bsdconfig" 32. $BSDCFG_SHARE/common.subr || exit 1 33f_dprintf "%s: loading includes..." mustberoot.subr 34f_include $BSDCFG_SHARE/dialog.subr 35 36BSDCFG_LIBE="/usr/libexec/bsdconfig" 37f_include_lang $BSDCFG_LIBE/include/messages.subr 38 39############################################################ CONFIGURATION 40# NOTE: These are not able to be overridden/inherited for security purposes. 41 42# 43# Number of tries a user gets to enter his/her password before we log the 44# sudo(8) failure and exit. 45# 46PASSWD_TRIES=3 47 48# 49# While in SECURE mode, should authentication as `root' be allowed? Set to 50# non-NULL to enable authentication as `root', otherwise disabled. 51# 52# WARNING: 53# Unless using a custom sudo(8) configuration, user `root' should not be 54# allowed because no password is required to become `root' when already `root' 55# and therefore, any value entered as password will work. 56# 57SECURE_ALLOW_ROOT= 58 59# 60# While in SECURE mode, should we divulge (through error message) when the 61# requested authentication user does not exist? Set to non-NULL to enable, 62# otherwise a non-existent user is treated like an invalid password. 63# 64SECURE_DIVULGE_UNKNOWN_USER= 65 66############################################################ FUNCTIONS 67 68# f_become_root_via_sudo 69# 70# If not running as root, prompt for sudo(8) credentials to become root. 71# Re-execution of the current program via sudo is automatically handled. 72# 73# The following environment variables effect functionality: 74# 75# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 76# that Xdialog(1) should be used instead of dialog(1). 77# 78f_become_root_via_sudo() 79{ 80 local msg hline size 81 82 [ "$( id -u )" = "0" ] && return $SUCCESS 83 84 f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 85 86 # 87 # Ask the user if it's OK to become root via sudo(8) and give them 88 # the option to save this preference (by touch(1)ing a file in the 89 # user's $HOME directory). 90 # 91 local checkpath="${HOME%/}/.bsdconfig_uses_sudo" 92 if [ ! -e "$checkpath" ]; then 93 msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" ) 94 local menu_list=" 95 'X' '$msg_cancel_exit' 96 '1' '$msg' 97 '2' '$msg_try_sudo_only_this_once' 98 " # END-QUOTE 99 msg=$( printf "$msg_you_are_not_root_but" bsdconfig ) 100 hline="$hline_arrows_tab_enter" 101 size=$( eval f_dialog_menu_size \ 102 \"\$DIALOG_TITLE\" \ 103 \"\$DIALOG_BACKTITLE\" \ 104 \"\$msg\" \ 105 \"\$hline\" \ 106 $menu_list ) 107 108 local dialog_menu mtag retval 109 dialog_menu=$( eval $DIALOG \ 110 --title \"\$DIALOG_TITLE\" \ 111 --backtitle \"\$DIALOG_BACKTITLE\" \ 112 --hline \"\$hline\" \ 113 --ok-label \"\$msg_ok\" \ 114 --cancel-label \"\$msg_cancel\" \ 115 --menu \"\$msg\" $size \ 116 $menu_list \ 117 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 118 ) 119 retval=$? 120 setvar DIALOG_MENU_$$ "$dialog_menu" 121 mtag=$( f_dialog_menutag ) 122 123 [ $retval -eq 0 ] || f_die 124 125 case "$mtag" in 126 X) # Cancel/Exit 127 f_die ;; 128 1) # Always try sudo(8) when run as $user 129 local err 130 if ! err=$( touch "$checkpath" 2>&1 ); then 131 f_show_msg "%s" "$err" 132 else 133 f_show_msg "$msg_created_path" "$checkpath" 134 fi 135 esac 136 else 137 # 138 # This user has created the path signing-off on sudo(8)-use 139 # but let's still give them a short/quick/unobtrusive reminder 140 # 141 f_dialog_info "$msg_becoming_root_via_sudo" 142 [ "$USE_XDIALOG" ] || sleep 0.6 143 fi 144 145 # 146 # Check sudo(8) access before prompting for password. 147 # 148 :| sudo -S -v 2> /dev/null 149 if [ $? -ne $SUCCESS ]; then 150 # 151 # sudo(8) access denied. Prompt for their password. 152 # 153 msg="$msg_please_enter_password" 154 hline="$hline_alnum_punc_tab_enter" 155 size=$( f_dialog_inputbox_size \ 156 "$DIALOG_TITLE" \ 157 "$DIALOG_BACKTITLE" \ 158 "$msg" \ 159 "$hline" ) 160 161 # 162 # Continue prompting until they either Cancel, succeed 163 # or exceed the number of allowed failures. 164 # 165 local password nfailures=0 retval 166 while [ $nfailures -lt $PASSWD_TRIES ]; do 167 if [ "$USE_XDIALOG" ]; then 168 password=$( $DIALOG \ 169 --title "$DIALOG_TITLE" \ 170 --backtitle "$DIALOG_BACKTITLE" \ 171 --hline "$hline" \ 172 --ok-label "$msg_ok" \ 173 --cancel-label "$msg_cancel" \ 174 --password --inputbox "$msg" $size \ 175 2>&1 > /dev/null ) 176 retval=$? 177 178 # Catch X11-related errors 179 [ $retval -eq 255 ] && 180 f_die $retval "$password" 181 else 182 local dialog_inputbox 183 dialog_inputbox=$( $DIALOG \ 184 --title "$DIALOG_TITLE" \ 185 --backtitle "$DIALOG_BACKTITLE" \ 186 --hline "$hline" \ 187 --ok-label "$msg_ok" \ 188 --cancel-label "$msg_cancel" \ 189 --insecure \ 190 --passwordbox "$msg" $size \ 191 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 192 ) 193 retval=$? 194 setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" 195 password=$( f_dialog_inputstr ) 196 fi 197 198 # Exit if the user cancelled. 199 [ $retval -eq $SUCCESS ] || exit $retval 200 201 # 202 # Validate sudo(8) credentials 203 # 204 sudo -S -v 2> /dev/null <<-EOF 205 $password 206 EOF 207 retval=$? 208 unset password # scrub memory 209 if [ $retval -eq $SUCCESS ]; then 210 # Access granted... 211 break 212 else 213 # Access denied... 214 nfailures=$(( $nfailures + 1 )) 215 216 # introduce a short delay 217 if [ $nfailures -lt $PASSWD_TRIES ]; then 218 f_dialog_info "$msg_sorry_try_again" 219 sleep 1 220 fi 221 fi 222 done 223 224 # 225 # If user exhausted number of allowed password tries, log 226 # the security event and exit immediately. 227 # 228 if [ $nfailures -ge $PASSWD_TRIES ]; then 229 msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 230 logger -p auth.notice -t sudo " " \ 231 "$USER : $msg" \ 232 "; TTY=$(tty)" \ 233 "; PWD=$PWD" \ 234 "; USER=root" \ 235 "; COMMAND=$0" 236 f_die 1 "sudo: $msg" 237 fi 238 fi 239 240 # Use xauth(1) to grant root the ability to use this X11/SSH session 241 if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 242 f_have xauth || f_die 1 \ 243 "$msg_no_such_file_or_directory" "$pgm" "xauth" 244 local HOSTNAME displaynum 245 HOSTNAME=$(hostname) 246 displaynum="${DISPLAY#*:}" 247 xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 248 $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 249 ~root/.Xauthority merge - > /dev/null 2>&1' 250 fi 251 252 # Re-execute ourselves with sudo(8) 253 if [ $ARGC -gt 0 ]; then 254 exec sudo "$0" $ARGV 255 else 256 exec sudo "$0" 257 fi 258 exit $? # Never reached unless error 259} 260 261# f_authenticate_some_user 262# 263# Only used if running as root and requires X11 (see USE_XDIALOG below). 264# Prompts the user to enter a username and password to be authenticated via 265# sudo(8) to proceed. 266# 267# The following environment variables effect functionality: 268# 269# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 270# that Xdialog(1) should be used instead of dialog(1). 271# 272f_authenticate_some_user() 273{ 274 local msg hline size width height 275 276 f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 277 278 # 279 # Secure-mode has been requested. 280 # 281 282 [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 283 [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 284 285 # 286 # Prompt for sudo(8) credentials. 287 # 288 289 msg="$msg_please_enter_username_password" 290 hline="$hline_alnum_punc_tab_enter" 291 size=$( f_xdialog_2inputsbox_size \ 292 "$DIALOG_TITLE" \ 293 "$DIALOG_BACKTITLE" \ 294 "$msg" \ 295 "$field_username" "" \ 296 "$field_password" "" ) 297 width="${size##*[$IFS]}" 298 height="${size%%[$IFS]*}" 299 height=$(( $height + 2 )) # Add height for --password 300 301 # 302 # Continue prompting until they either Cancel, succeed or exceed the 303 # number of allowed failures. 304 # 305 local user_pass nfailures=0 retval 306 while [ $nfailures -lt $PASSWD_TRIES ]; do 307 user_pass=$( $DIALOG \ 308 --title "$DIALOG_TITLE" \ 309 --backtitle "$DIALOG_BACKTITLE" \ 310 --hline "$hline" \ 311 --ok-label "$msg_ok" \ 312 --cancel-label "$msg_cancel" \ 313 --password --2inputsbox "$msg" \ 314 $height $width \ 315 "$field_username" "" \ 316 "$field_password" "" \ 317 2>&1 > /dev/null ) 318 retval=$? 319 320 # Catch X11-related errors 321 [ $retval -eq 255 ] && f_die $retval "$user_pass" 322 323 # Exit if the user cancelled. 324 [ $retval -eq $SUCCESS ] || exit $retval 325 326 # 327 # Make sure the user exists and is non-root 328 # 329 local user password 330 user="${user_pass%%/*}" 331 password="${user_pass#*/}" 332 unset user_pass # scrub memory 333 if [ ! "$user" ]; then 334 nfailures=$(( $nfailures + 1 )) 335 f_dialog_msgbox "$msg_no_username" 336 continue 337 fi 338 if [ ! "$SECURE_ALLOW_ROOT" ]; then 339 case "$user" in 340 root|toor) 341 nfailures=$(( $nfailures + 1 )) 342 f_show_msg "$msg_user_disallowed" "$user" 343 continue 344 esac 345 fi 346 if ! f_quietly id "$user"; then 347 nfailures=$(( $nfailures + 1 )) 348 if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 349 f_show_msg "$msg_unknown_user" "$user" 350 elif [ $nfailures -lt $PASSWD_TRIES ]; then 351 f_dialog_info "$msg_sorry_try_again" 352 sleep 1 353 fi 354 continue 355 fi 356 357 # 358 # Validate sudo(8) credentials for given user 359 # 360 su -m "$user" <<-EOF 361 sh <<EOS 362 sudo -k 363 sudo -S -v 2> /dev/null <<EOP 364 $password 365 EOP 366 EOS 367 EOF 368 retval=$? 369 unset user 370 unset password # scrub memory 371 372 if [ $retval -eq $SUCCESS ]; then 373 # Access granted... 374 break 375 else 376 # Access denied... 377 nfailures=$(( $nfailures + 1 )) 378 379 # introduce a short delay 380 if [ $nfailures -lt $PASSWD_TRIES ]; then 381 f_dialog_info "$msg_sorry_try_again" 382 sleep 1 383 fi 384 fi 385 done 386 387 # 388 # If user exhausted number of allowed password tries, log 389 # the security event and exit immediately. 390 # 391 if [ $nfailures -ge $PASSWD_TRIES ]; then 392 msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 393 logger -p auth.notice -t sudo " " \ 394 "${SUDO_USER:-$USER} : $msg" \ 395 "; TTY=$(tty)" \ 396 "; PWD=$PWD" \ 397 "; USER=root" \ 398 "; COMMAND=$0" 399 f_die 1 "sudo: $message" 400 fi 401} 402 403# f_mustberoot_init 404# 405# If not already root, make the switch to root by re-executing ourselves via 406# sudo(8) using user-supplied credentials. 407# 408# The following environment variables effect functionality: 409# 410# SECURE Either NULL or Non-NULL. If given a value will indicate 411# that (while running as root) sudo(8) authentication is 412# required to proceed. 413# 414f_mustberoot_init() 415{ 416 if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 417 f_become_root_via_sudo 418 elif [ "$SECURE" ]; then 419 f_authenticate_some_user 420 fi 421} 422 423############################################################ MAIN 424 425f_dprintf "%s: Successfully loaded." mustberoot.subr 426 427fi # ! $_MUSTBEROOT_SUBR 428