mustberoot.subr revision 240783
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 240783 2012-09-21 19:03:25Z 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 # Check sudo(8) access before prompting for password. 87 # 88 :| sudo -S -v 2> /dev/null 89 if [ $? -ne $SUCCESS ]; then 90 # 91 # sudo(8) access denied. Prompt for their password. 92 # 93 msg="$msg_please_enter_password" 94 hline="$hline_alnum_punc_tab_enter" 95 size=$( f_dialog_inputbox_size \ 96 "$DIALOG_TITLE" \ 97 "$DIALOG_BACKTITLE" \ 98 "$msg" \ 99 "$hline" ) 100 101 # 102 # Continue prompting until they either Cancel, succeed 103 # or exceed the number of allowed failures. 104 # 105 local password nfailures=0 retval 106 while [ $nfailures -lt $PASSWD_TRIES ]; do 107 if [ "$USE_XDIALOG" ]; then 108 password=$( $DIALOG \ 109 --title "$DIALOG_TITLE" \ 110 --backtitle "$DIALOG_BACKTITLE" \ 111 --hline "$hline" \ 112 --ok-label "$msg_ok" \ 113 --cancel-label "$msg_cancel" \ 114 --password --inputbox "$msg" $size \ 115 2>&1 > /dev/null ) 116 retval=$? 117 118 # Catch X11-related errors 119 [ $retval -eq 255 ] && 120 f_die $retval "$password" 121 else 122 local dialog_inputbox 123 dialog_inputbox=$( $DIALOG \ 124 --title "$DIALOG_TITLE" \ 125 --backtitle "$DIALOG_BACKTITLE" \ 126 --hline "$hline" \ 127 --ok-label "$msg_ok" \ 128 --cancel-label "$msg_cancel" \ 129 --insecure \ 130 --passwordbox "$msg" $size \ 131 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD 132 ) 133 retval=$? 134 setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox" 135 password=$( f_dialog_inputstr ) 136 fi 137 138 # Exit if the user cancelled. 139 [ $retval -eq $SUCCESS ] || exit $retval 140 141 # 142 # Validate sudo(8) credentials 143 # 144 sudo -S -v 2> /dev/null <<-EOF 145 $password 146 EOF 147 retval=$? 148 unset password # scrub memory 149 if [ $retval -eq $SUCCESS ]; then 150 # Access granted... 151 break 152 else 153 # Access denied... 154 nfailures=$(( $nfailures + 1 )) 155 156 # introduce a short delay 157 if [ $nfailures -lt $PASSWD_TRIES ]; then 158 f_dialog_info "$msg_sorry_try_again" 159 sleep 1 160 fi 161 fi 162 done 163 164 # 165 # If user exhausted number of allowed password tries, log 166 # the security event and exit immediately. 167 # 168 if [ $nfailures -ge $PASSWD_TRIES ]; then 169 msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 170 logger -p auth.notice -t sudo " " \ 171 "$USER : $msg" \ 172 "; TTY=$(tty)" \ 173 "; PWD=$PWD" \ 174 "; USER=root" \ 175 "; COMMAND=$0" 176 f_die 1 "sudo: $msg" 177 fi 178 fi 179 180 # Use xauth(1) to grant root the ability to use this X11/SSH session 181 if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then 182 f_have xauth || f_die 1 \ 183 "$msg_no_such_file_or_directory" "$pgm" "xauth" 184 local HOSTNAME displaynum 185 HOSTNAME=$(hostname) 186 displaynum="${DISPLAY#*:}" 187 xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \ 188 $HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \ 189 ~root/.Xauthority merge - > /dev/null 2>&1' 190 fi 191 192 # Re-execute ourselves with sudo(8) 193 if [ $ARGC -gt 0 ]; then 194 exec sudo "$0" $ARGV 195 else 196 exec sudo "$0" 197 fi 198 exit $? # Never reached unless error 199} 200 201# f_authenticate_some_user 202# 203# Only used if running as root and requires X11 (see USE_XDIALOG below). 204# Prompts the user to enter a username and password to be authenticated via 205# sudo(8) to proceed. 206# 207# The following environment variables effect functionality: 208# 209# USE_XDIALOG Either NULL or Non-NULL. If given a value will indicate 210# that Xdialog(1) should be used instead of dialog(1). 211# 212f_authenticate_some_user() 213{ 214 local msg hline size width height 215 216 f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm" 217 218 # 219 # Secure-mode has been requested. 220 # 221 222 [ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11" 223 [ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root" 224 225 # 226 # Prompt for sudo(8) credentials. 227 # 228 229 msg="$msg_please_enter_username_password" 230 hline="$hline_alnum_punc_tab_enter" 231 size=$( f_xdialog_2inputsbox_size \ 232 "$DIALOG_TITLE" \ 233 "$DIALOG_BACKTITLE" \ 234 "$msg" \ 235 "$field_username" "" \ 236 "$field_password" "" ) 237 width="${size##*[$IFS]}" 238 height="${size%%[$IFS]*}" 239 height=$(( $height + 2 )) # Add height for --password 240 241 # 242 # Continue prompting until they either Cancel, succeed or exceed the 243 # number of allowed failures. 244 # 245 local user_pass nfailures=0 retval 246 while [ $nfailures -lt $PASSWD_TRIES ]; do 247 user_pass=$( $DIALOG \ 248 --title "$DIALOG_TITLE" \ 249 --backtitle "$DIALOG_BACKTITLE" \ 250 --hline "$hline" \ 251 --ok-label "$msg_ok" \ 252 --cancel-label "$msg_cancel" \ 253 --password --2inputsbox "$msg" \ 254 $height $width \ 255 "$field_username" "" \ 256 "$field_password" "" \ 257 2>&1 > /dev/null ) 258 retval=$? 259 260 # Catch X11-related errors 261 [ $retval -eq 255 ] && f_die $retval "$user_pass" 262 263 # Exit if the user cancelled. 264 [ $retval -eq $SUCCESS ] || exit $retval 265 266 # 267 # Make sure the user exists and is non-root 268 # 269 local user password 270 user="${user_pass%%/*}" 271 password="${user_pass#*/}" 272 unset user_pass # scrub memory 273 if [ ! "$user" ]; then 274 nfailures=$(( $nfailures + 1 )) 275 f_dialog_msgbox "$msg_no_username" 276 continue 277 fi 278 if [ ! "$SECURE_ALLOW_ROOT" ]; then 279 case "$user" in 280 root|toor) 281 nfailures=$(( $nfailures + 1 )) 282 f_dialog_msgbox "$( printf \ 283 "$msg_user_disallowed" "$user" )" 284 continue 285 esac 286 fi 287 if ! f_quietly id "$user"; then 288 nfailures=$(( $nfailures + 1 )) 289 if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then 290 f_dialog_msgbox "$( printf \ 291 "$msg_unknown_user" "$user" )" 292 elif [ $nfailures -lt $PASSWD_TRIES ]; then 293 f_dialog_info "$msg_sorry_try_again" 294 sleep 1 295 fi 296 continue 297 fi 298 299 # 300 # Validate sudo(8) credentials for given user 301 # 302 su -m "$user" <<-EOF 303 sh <<EOS 304 sudo -k 305 sudo -S -v 2> /dev/null <<EOP 306 $password 307 EOP 308 EOS 309 EOF 310 retval=$? 311 unset user 312 unset password # scrub memory 313 314 if [ $retval -eq $SUCCESS ]; then 315 # Access granted... 316 break 317 else 318 # Access denied... 319 nfailures=$(( $nfailures + 1 )) 320 321 # introduce a short delay 322 if [ $nfailures -lt $PASSWD_TRIES ]; then 323 f_dialog_info "$msg_sorry_try_again" 324 sleep 1 325 fi 326 fi 327 done 328 329 # 330 # If user exhausted number of allowed password tries, log 331 # the security event and exit immediately. 332 # 333 if [ $nfailures -ge $PASSWD_TRIES ]; then 334 msg=$( printf "$msg_nfailed_attempts" "$nfailures" ) 335 logger -p auth.notice -t sudo " " \ 336 "${SUDO_USER:-$USER} : $msg" \ 337 "; TTY=$(tty)" \ 338 "; PWD=$PWD" \ 339 "; USER=root" \ 340 "; COMMAND=$0" 341 f_die 1 "sudo: $message" 342 fi 343} 344 345# f_mustberoot_init 346# 347# If not already root, make the switch to root by re-executing ourselves via 348# sudo(8) using user-supplied credentials. 349# 350# The following environment variables effect functionality: 351# 352# SECURE Either NULL or Non-NULL. If given a value will indicate 353# that (while running as root) sudo(8) authentication is 354# required to proceed. 355# 356f_mustberoot_init() 357{ 358 if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then 359 f_become_root_via_sudo 360 elif [ "$SECURE" ]; then 361 f_authenticate_some_user 362 fi 363} 364 365fi # ! $_MUSTBEROOT_SUBR 366