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