mustberoot.subr revision 251266
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 (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 251266 2013-06-02 22:34:40Z 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 prompt hline height width rows msg 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 prompt=$( printf "$msg_you_are_not_root_but" bsdconfig ) 94 msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" ) 95 local menu_list=" 96 'X' '$msg_cancel_exit' 97 '1' '$msg' 98 '2' '$msg_try_sudo_only_this_once' 99 " # END-QUOTE 100 hline="$hline_arrows_tab_enter" 101 102 eval f_dialog_menu_size height width rows \ 103 \"\$DIALOG_TITLE\" \ 104 \"\$DIALOG_BACKTITLE\" \ 105 \"\$prompt\" \ 106 \"\$hline\" \ 107 $menu_list 108 109 local mtag 110 mtag=$( eval $DIALOG \ 111 --title \"\$DIALOG_TITLE\" \ 112 --backtitle \"\$DIALOG_BACKTITLE\" \ 113 --hline \"\$hline\" \ 114 --ok-label \"\$msg_ok\" \ 115 --cancel-label \"\$msg_cancel\" \ 116 --menu \"\$prompt\" \ 117 $height $width $rows \ 118 $menu_list \ 119 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 120 ) || f_die 121 f_dialog_data_sanitize mtag 122 123 case "$mtag" in 124 X) # Cancel/Exit 125 f_die ;; 126 1) # Always try sudo(8) when run as $user 127 local err 128 if ! err=$( touch "$checkpath" 2>&1 ); then 129 f_dialog_msgbox "$err" 130 else 131 f_show_msg "$msg_created_path" "$checkpath" 132 fi 133 esac 134 else 135 # 136 # This user has created the path signing-off on sudo(8)-use 137 # but let's still give them a short/quick/unobtrusive reminder 138 # 139 f_dialog_info "$msg_becoming_root_via_sudo" 140 [ "$USE_XDIALOG" ] || sleep 0.6 141 fi 142 143 # 144 # Check sudo(8) access before prompting for password. 145 # 146 :| sudo -S -v 2> /dev/null 147 if [ $? -ne $SUCCESS ]; then 148 # 149 # sudo(8) access denied. Prompt for their password. 150 # 151 prompt="$msg_please_enter_password" 152 hline="$hline_alnum_punc_tab_enter" 153 f_dialog_inputbox_size height width \ 154 "$DIALOG_TITLE" \ 155 "$DIALOG_BACKTITLE" \ 156 "$prompt" \ 157 "$hline" 158 159 # 160 # Continue prompting until they either Cancel, succeed 161 # or exceed the number of allowed failures. 162 # 163 local password nfailures=0 retval 164 while [ $nfailures -lt $PASSWD_TRIES ]; do 165 if [ "$USE_XDIALOG" ]; then 166 password=$( $DIALOG \ 167 --title "$DIALOG_TITLE" \ 168 --backtitle "$DIALOG_BACKTITLE" \ 169 --hline "$hline" \ 170 --ok-label "$msg_ok" \ 171 --cancel-label "$msg_cancel" \ 172 --password --inputbox "$prompt" \ 173 $height $width \ 174 2>&1 > /dev/null 175 ) 176 retval=$? 177 178 # Catch X11-related errors 179 [ $retval -eq 255 ] && 180 f_die $retval "$password" 181 else 182 password=$( $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 "$prompt" \ 190 $height $width \ 191 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 192 ) || exit $? 193 fi 194 debug= f_dialog_line_sanitize password 195 196 # 197 # Validate sudo(8) credentials 198 # 199 sudo -S -v 2> /dev/null <<-EOF 200 $password 201 EOF 202 retval=$? 203 unset password # scrub memory 204 if [ $retval -eq $SUCCESS ]; then 205 # Access granted... 206 break 207 else 208 # Access denied... 209 nfailures=$(( $nfailures + 1 )) 210 211 # introduce a short delay 212 if [ $nfailures -lt $PASSWD_TRIES ]; then 213 f_dialog_info "$msg_sorry_try_again" 214 sleep 1 215 fi 216 fi 217 done 218 219 # 220 # If user exhausted number of allowed password tries, log 221 # the security event and exit immediately. 222 # 223 if [ $nfailures -ge $PASSWD_TRIES ]; then 224 msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 225 logger -p auth.notice -t sudo " " \ 226 "$USER : $msg" \ 227 "; TTY=$(tty)" \ 228 "; PWD=$PWD" \ 229 "; USER=root" \ 230 "; COMMAND=$0" 231 f_die 1 "sudo: $msg" 232 fi 233 fi 234 235 # Use xauth(1) to grant root the ability to use this X11/SSH session 236 if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 237 f_have xauth || f_die 1 \ 238 "$msg_no_such_file_or_directory" "$pgm" "xauth" 239 local HOSTNAME displaynum 240 HOSTNAME=$(hostname) 241 displaynum="${DISPLAY#*:}" 242 xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 243 $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 244 ~root/.Xauthority merge - > /dev/null 2>&1' 245 fi 246 247 # Re-execute ourselves with sudo(8) 248 f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr 249 if [ $ARGC -gt 0 ]; then 250 exec sudo "$0" $ARGV 251 else 252 exec sudo "$0" 253 fi 254 exit $? # Never reached unless error 255} 256 257# f_authenticate_some_user 258# 259# Only used if running as root and requires X11 (see USE_XDIALOG below). 260# Prompts the user to enter a username and password to be authenticated via 261# sudo(8) to proceed. 262# 263# The following environment variables effect functionality: 264# 265# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 266# that Xdialog(1) should be used instead of dialog(1). 267# 268f_authenticate_some_user() 269{ 270 local msg hline height width 271 272 f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 273 274 # 275 # Secure-mode has been requested. 276 # 277 278 [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 279 [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 280 281 # 282 # Prompt for sudo(8) credentials. 283 # 284 285 msg="$msg_please_enter_username_password" 286 hline="$hline_alnum_punc_tab_enter" 287 f_xdialog_2inputsbox_size height width \ 288 "$DIALOG_TITLE" \ 289 "$DIALOG_BACKTITLE" \ 290 "$msg" \ 291 "$field_username" "" \ 292 "$field_password" "" 293 height=$(( $height + 2 )) # Add height for --password 294 295 # 296 # Continue prompting until they either Cancel, succeed or exceed the 297 # number of allowed failures. 298 # 299 local user_pass nfailures=0 retval 300 while [ $nfailures -lt $PASSWD_TRIES ]; do 301 user_pass=$( $DIALOG \ 302 --title "$DIALOG_TITLE" \ 303 --backtitle "$DIALOG_BACKTITLE" \ 304 --hline "$hline" \ 305 --ok-label "$msg_ok" \ 306 --cancel-label "$msg_cancel" \ 307 --password --2inputsbox "$msg" \ 308 $height $width \ 309 "$field_username" "" \ 310 "$field_password" "" \ 311 2>&1 > /dev/null ) 312 retval=$? 313 314 # Catch X11-related errors 315 [ $retval -eq 255 ] && f_die $retval "$user_pass" 316 317 # Exit if the user cancelled. 318 [ $retval -eq $SUCCESS ] || exit $retval 319 320 # 321 # Make sure the user exists and is non-root 322 # 323 local user password 324 user="${user_pass%%/*}" 325 password="${user_pass#*/}" 326 unset user_pass # scrub memory 327 if [ ! "$user" ]; then 328 nfailures=$(( $nfailures + 1 )) 329 f_dialog_msgbox "$msg_no_username" 330 continue 331 fi 332 if [ ! "$SECURE_ALLOW_ROOT" ]; then 333 case "$user" in 334 root|toor) 335 nfailures=$(( $nfailures + 1 )) 336 f_show_msg "$msg_user_disallowed" "$user" 337 continue 338 esac 339 fi 340 if ! f_quietly id "$user"; then 341 nfailures=$(( $nfailures + 1 )) 342 if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 343 f_show_msg "$msg_unknown_user" "$user" 344 elif [ $nfailures -lt $PASSWD_TRIES ]; then 345 f_dialog_info "$msg_sorry_try_again" 346 sleep 1 347 fi 348 continue 349 fi 350 351 # 352 # Validate sudo(8) credentials for given user 353 # 354 su -m "$user" <<-EOF 355 sh <<EOS 356 sudo -k 357 sudo -S -v 2> /dev/null <<EOP 358 $password 359 EOP 360 EOS 361 EOF 362 retval=$? 363 unset user 364 unset password # scrub memory 365 366 if [ $retval -eq $SUCCESS ]; then 367 # Access granted... 368 break 369 else 370 # Access denied... 371 nfailures=$(( $nfailures + 1 )) 372 373 # introduce a short delay 374 if [ $nfailures -lt $PASSWD_TRIES ]; then 375 f_dialog_info "$msg_sorry_try_again" 376 sleep 1 377 fi 378 fi 379 done 380 381 # 382 # If user exhausted number of allowed password tries, log 383 # the security event and exit immediately. 384 # 385 if [ $nfailures -ge $PASSWD_TRIES ]; then 386 msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 387 logger -p auth.notice -t sudo " " \ 388 "${SUDO_USER:-$USER} : $msg" \ 389 "; TTY=$(tty)" \ 390 "; PWD=$PWD" \ 391 "; USER=root" \ 392 "; COMMAND=$0" 393 f_die 1 "sudo: $message" 394 fi 395} 396 397# f_mustberoot_init 398# 399# If not already root, make the switch to root by re-executing ourselves via 400# sudo(8) using user-supplied credentials. 401# 402# The following environment variables effect functionality: 403# 404# SECURE Either NULL or Non-NULL. If given a value will indicate 405# that (while running as root) sudo(8) authentication is 406# required to proceed. 407# 408f_mustberoot_init() 409{ 410 if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 411 f_become_root_via_sudo 412 elif [ "$SECURE" ]; then 413 f_authenticate_some_user 414 fi 415} 416 417############################################################ MAIN 418 419f_dprintf "%s: Successfully loaded." mustberoot.subr 420 421fi # ! $_MUSTBEROOT_SUBR 422