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