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