mustberoot.subr revision 238438
167754Smsmithif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
267754Smsmith#
377424Smsmith# Copyright (c) 2006-2012 Devin Teske
482367Smsmith# All Rights Reserved.
567754Smsmith#
667754Smsmith# Redistribution and use in source and binary forms, with or without
767754Smsmith# modification, are permitted provided that the following conditions
867754Smsmith# are met:
967754Smsmith# 1. Redistributions of source code must retain the above copyright
1067754Smsmith#    notice, this list of conditions and the following disclaimer.
1167754Smsmith# 2. Redistributions in binary form must reproduce the above copyright
1271867Smsmith#    notice, this list of conditions and the following disclaimer in the
1370243Smsmith#    documentation and/or other materials provided with the distribution.
1467754Smsmith#
1567754Smsmith# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1667754Smsmith# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
1767754Smsmith# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1867754Smsmith# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1967754Smsmith# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2067754Smsmith# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2167754Smsmith# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2267754Smsmith# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2367754Smsmith# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2467754Smsmith# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2567754Smsmith# SUCH DAMAGE.
2667754Smsmith#
2767754Smsmith# $FreeBSD: head/usr.sbin/bsdconfig/include/mustberoot.subr 238438 2012-07-14 03:16:57Z dteske $
2867754Smsmith#
2967754Smsmith############################################################ INCLUDES
3067754Smsmith
3167754SmsmithBSDCFG_LIBE="/usr/libexec/bsdconfig"
3267754Smsmith. $BSDCFG_LIBE/include/common.subr || exit 1
3367754Smsmithf_include $BSDCFG_LIBE/include/dialog.subr
3467754Smsmithf_include_lang $BSDCFG_LIBE/include/messages.subr
3567754Smsmith
3667754Smsmith############################################################ CONFIGURATION
3767754Smsmith# NOTE: These are not able to be overridden/inherited for security purposes.
3867754Smsmith
3967754Smsmith#
4067754Smsmith# Number of tries a user gets to enter his/her password before we log the
4167754Smsmith# sudo(8) failure and exit.
4267754Smsmith#
4367754SmsmithPASSWD_TRIES=3
4467754Smsmith
4567754Smsmith#
4667754Smsmith# While in SECURE mode, should authentication as `root' be allowed? Set to
4767754Smsmith# non-NULL to enable authentication as `root', otherwise disabled.
4867754Smsmith#
4967754Smsmith# WARNING: 
5067754Smsmith# Unless using a custom sudo(8) configuration, user `root' should not be
5167754Smsmith# allowed because no password is required to become `root' when already `root'
5267754Smsmith# and therefore, any value entered as password will work.
5367754Smsmith#
5467754SmsmithSECURE_ALLOW_ROOT=
5567754Smsmith
5667754Smsmith#
5767754Smsmith# While in SECURE mode, should we divulge (through error message) when the
5867754Smsmith# requested authentication user does not exist? Set to non-NULL to enable,
5967754Smsmith# otherwise a non-existent user is treated like an invalid password.
6067754Smsmith#
6167754SmsmithSECURE_DIVULGE_UNKNOWN_USER=
6267754Smsmith
6367754Smsmith############################################################ FUNCTIONS
6467754Smsmith
6567754Smsmith# f_become_root_via_sudo
6667754Smsmith#
6767754Smsmith# If not running as root, prompt for sudo(8) credentials to become root.
6867754Smsmith# Re-execution of the current program via sudo is automatically handled.
6967754Smsmith#
7067754Smsmith# The following environment variables effect functionality:
7167754Smsmith#
7267754Smsmith# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
7367754Smsmith# 	              that Xdialog(1) should be used instead of dialog(1).
7467754Smsmith#
7567754Smsmithf_become_root_via_sudo()
7667754Smsmith{
7767754Smsmith	local msg hline size
7867754Smsmith
7967754Smsmith	[ "$( id -u )" = "0" ] && return $SUCCESS
8067754Smsmith
8167754Smsmith	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
8267754Smsmith
8367754Smsmith	#
8467754Smsmith	# Check sudo(8) access before prompting for password.
8567754Smsmith	#
8667754Smsmith	:| sudo -S -v 2> /dev/null
8767754Smsmith	if [ $? -ne $SUCCESS ]; then
8867754Smsmith		#
8967754Smsmith		# sudo(8) access denied. Prompt for their password.
9067754Smsmith		#
9167754Smsmith		msg="$msg_please_enter_password"
9267754Smsmith		hline="$hline_alnum_punc_tab_enter"
9367754Smsmith		size=$( f_dialog_inputbox_size \
9467754Smsmith		        	"$DIALOG_TITLE"     \
9567754Smsmith		        	"$DIALOG_BACKTITLE" \
9667754Smsmith		        	"$msg"              \
9767754Smsmith		        	"$hline"            )
9867754Smsmith
9967754Smsmith		#
10067754Smsmith		# Continue prompting until they either Cancel, succeed
10167754Smsmith		# or exceed the number of allowed failures.
10267754Smsmith		#
10367754Smsmith		local password nfailures=0 retval
10467754Smsmith		while [ $nfailures -lt $PASSWD_TRIES ]; do
10567754Smsmith			if [ "$USE_XDIALOG" ]; then
10667754Smsmith				password=$( $DIALOG \
10767754Smsmith					--title "$DIALOG_TITLE"            \
10867754Smsmith					--backtitle "$DIALOG_BACKTITLE"    \
10967754Smsmith					--hline "$hline"                   \
11067754Smsmith					--ok-label "$msg_ok"               \
11167754Smsmith					--cancel-label "$msg_cancel"       \
11267754Smsmith					--password --inputbox "$msg" $size \
11367754Smsmith					2>&1 > /dev/null )
11467754Smsmith				retval=$?
11567754Smsmith
11667754Smsmith				# Catch X11-related errors
11777424Smsmith				[ $retval -eq 255 ] &&
11867754Smsmith					f_die $retval "$password"
11967754Smsmith			else
12067754Smsmith				$DIALOG \
12167754Smsmith					--title "$DIALOG_TITLE"         \
12267754Smsmith					--backtitle "$DIALOG_BACKTITLE" \
12367754Smsmith					--hline "$hline"                \
12469746Smsmith					--ok-label "$msg_ok"            \
12567754Smsmith					--cancel-label "$msg_cancel"    \
12667754Smsmith					--insecure                      \
12777424Smsmith					--passwordbox "$msg" $size      \
12877424Smsmith					2> "$DIALOG_TMPDIR/dialog.inputbox.$$"
12967754Smsmith				retval=$?
13067754Smsmith				password=$( f_dialog_inputstr )
13180062Smsmith			fi
13267754Smsmith
13367754Smsmith			# Exit if the user cancelled.
13480062Smsmith			[ $retval -eq $SUCCESS ] || exit $retval
13580062Smsmith
13680062Smsmith			#
13780062Smsmith			# Validate sudo(8) credentials
13880062Smsmith			#
13980062Smsmith			sudo -S -v 2> /dev/null <<-EOF
14080062Smsmith			$password
14180062Smsmith			EOF
14280062Smsmith			retval=$?
14380062Smsmith			unset password # scrub memory
14480062Smsmith			if [ $retval -eq $SUCCESS ]; then
14580062Smsmith				# Access granted...
14680062Smsmith				break
14780062Smsmith			else
14880062Smsmith				# Access denied...
14980062Smsmith				nfailures=$(( $nfailures + 1 ))
15080062Smsmith
15180062Smsmith				# introduce a short delay
15280062Smsmith				if [ $nfailures -lt $PASSWD_TRIES ]; then
15380062Smsmith					f_dialog_info "$msg_sorry_try_again"
15480062Smsmith					sleep 1
15580062Smsmith				fi
15680062Smsmith			fi
15780062Smsmith		done
15880062Smsmith
15980062Smsmith		#
16080062Smsmith		# If user exhausted number of allowed password tries, log
16180062Smsmith		# the security event and exit immediately.
16280062Smsmith		#
16380062Smsmith		if [ $nfailures -ge $PASSWD_TRIES ]; then
16480062Smsmith			msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
16580062Smsmith			logger -p auth.notice -t sudo " " \
16680062Smsmith				"$USER : $msg" \
16780062Smsmith				"; TTY=$(tty)" \
16880062Smsmith				"; PWD=$PWD"   \
16980062Smsmith				"; USER=root"  \
17080062Smsmith				"; COMMAND=$0"
17180062Smsmith			f_die 1 "sudo: $msg"
17280062Smsmith		fi
17380062Smsmith	fi
17480062Smsmith
17580062Smsmith	# Use xauth(1) to grant root the ability to use this X11/SSH session
17680062Smsmith	if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then
17780062Smsmith		f_have xauth || f_die 1 \
17880062Smsmith			"$msg_no_such_file_or_directory" "$pgm" "xauth"
17980062Smsmith		local HOSTNAME displaynum
18080062Smsmith		HOSTNAME=$(hostname)
18180062Smsmith		displaynum="${DISPLAY#*:}"
18280062Smsmith		xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \
18380062Smsmith			$HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \
18480062Smsmith			~root/.Xauthority merge - > /dev/null 2>&1'
18580062Smsmith	fi
18680062Smsmith
18780062Smsmith	# Re-execute ourselves with sudo(8)
18880062Smsmith	if [ $ARGC -gt 0 ]; then
18980062Smsmith		exec sudo "$0" $ARGV
19080062Smsmith	else
19180062Smsmith		exec sudo "$0"
19280062Smsmith	fi
19380062Smsmith	exit $? # Never reached unless error
19480062Smsmith}
19580062Smsmith
19680062Smsmith# f_authenticate_some_user
19780062Smsmith#
19880062Smsmith# Only used if running as root and requires X11 (see USE_XDIALOG below).
19980062Smsmith# Prompts the user to enter a username and password to be authenticated via
20080062Smsmith# sudo(8) to proceed.
20180062Smsmith#
20280062Smsmith# The following environment variables effect functionality:
20380062Smsmith#
20480062Smsmith# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
20580062Smsmith# 	              that Xdialog(1) should be used instead of dialog(1).
20680062Smsmith#
20780062Smsmithf_authenticate_some_user()
20867754Smsmith{
20967754Smsmith	local msg hline size width height
21067754Smsmith
21167754Smsmith	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
21267754Smsmith
21367754Smsmith	#
21467754Smsmith	# Secure-mode has been requested.
21567754Smsmith	#
21667754Smsmith 
21767754Smsmith	[ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11"
21867754Smsmith	[ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root"
21967754Smsmith
22067754Smsmith	#
22167754Smsmith	# Prompt for sudo(8) credentials.
22267754Smsmith	#
22367754Smsmith
22467754Smsmith	msg="$msg_please_enter_username_password"
22567754Smsmith	hline="$hline_alnum_punc_tab_enter"
22667754Smsmith	size=$( f_xdialog_2inputsbox_size \
22777424Smsmith	        	"$DIALOG_TITLE"      \
22867754Smsmith	        	"$DIALOG_BACKTITLE"  \
22967754Smsmith	        	"$msg"               \
23067754Smsmith	        	"$field_username" "" \
23167754Smsmith	        	"$field_password" "" )
23267754Smsmith	width="${size##*[$IFS]}"
23367754Smsmith	height="${size%%[$IFS]*}"
23467754Smsmith	height=$(( $height + 2 )) # Add height for --password
23567754Smsmith
23667754Smsmith	#
23767754Smsmith	# Continue prompting until they either Cancel, succeed or exceed the
23867754Smsmith	# number of allowed failures.
23967754Smsmith	#
24067754Smsmith	local user_pass nfailures=0 retval
24167754Smsmith	while [ $nfailures -lt $PASSWD_TRIES ]; do
24267754Smsmith		user_pass=$( $DIALOG \
24367754Smsmith			--title "$DIALOG_TITLE"         \
24467754Smsmith			--backtitle "$DIALOG_BACKTITLE" \
24569450Smsmith			--hline "$hline"                \
24669450Smsmith			--ok-label "$msg_ok"            \
24769450Smsmith			--cancel-label "$msg_cancel"    \
24867754Smsmith			--password --2inputsbox "$msg"  \
24967754Smsmith			$height $width                  \
25067754Smsmith			"$field_username" ""            \
25167754Smsmith			"$field_password" ""            \
25267754Smsmith			2>&1 > /dev/null )
25367754Smsmith		retval=$?
25467754Smsmith
25567754Smsmith		# Catch X11-related errors
25667754Smsmith		[ $retval -eq 255 ] && f_die $retval "$user_pass"
25767754Smsmith
25867754Smsmith		# Exit if the user cancelled.
25967754Smsmith		[ $retval -eq $SUCCESS ] || exit $retval
26077424Smsmith
26177424Smsmith		#
26277424Smsmith		# Make sure the user exists and is non-root
26377424Smsmith		#
26467754Smsmith		local user password
26567754Smsmith		user="${user_pass%%/*}"
26667754Smsmith		password="${user_pass#*/}"
26767754Smsmith		unset user_pass # scrub memory
26867754Smsmith		if [ ! "$user" ]; then
26967754Smsmith			nfailures=$(( $nfailures + 1 ))
27077424Smsmith			f_dialog_msgbox "$msg_no_username"
27167754Smsmith			continue
27267754Smsmith		fi
27371867Smsmith		if [ ! "$SECURE_ALLOW_ROOT" ]; then
27467754Smsmith			case "$user" in
27567754Smsmith			root|toor)
27677424Smsmith				nfailures=$(( $nfailures + 1 ))
27767754Smsmith				f_dialog_msgbox "$( printf \
27867754Smsmith					"$msg_user_disallowed" "$user" )"
27967754Smsmith				continue
28067754Smsmith			esac
28167754Smsmith		fi
28267754Smsmith		if ! f_quietly id "$user"; then
28367754Smsmith			nfailures=$(( $nfailures + 1 ))
28467754Smsmith			if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
28567754Smsmith				f_dialog_msgbox "$( printf \
28667754Smsmith					"$msg_unknown_user" "$user" )"
28767754Smsmith			elif [ $nfailures -lt $PASSWD_TRIES ]; then
28867754Smsmith				f_dialog_info "$msg_sorry_try_again"
28967754Smsmith				sleep 1
29067754Smsmith			fi
29167754Smsmith			continue
29267754Smsmith		fi
29367754Smsmith
29467754Smsmith		#
29567754Smsmith		# Validate sudo(8) credentials for given user
29667754Smsmith		#
29767754Smsmith		su -m "$user" <<-EOF
29867754Smsmith			sh <<EOS
29967754Smsmith				sudo -k
30067754Smsmith				sudo -S -v 2> /dev/null <<EOP
30167754Smsmith				$password
30267754Smsmith				EOP
30367754Smsmith			EOS
30467754Smsmith		EOF
30567754Smsmith		retval=$?
30667754Smsmith		unset user
30767754Smsmith		unset password # scrub memory
30867754Smsmith
30967754Smsmith		if [ $retval -eq $SUCCESS ]; then
31067754Smsmith			# Access granted...
31167754Smsmith			break
31267754Smsmith		else
31378986Smsmith			# Access denied...
31478986Smsmith			nfailures=$(( $nfailures + 1 ))
31578986Smsmith
31678986Smsmith			# introduce a short delay
31778986Smsmith			if [ $nfailures -lt $PASSWD_TRIES ]; then
31878986Smsmith				f_dialog_info "$msg_sorry_try_again"
31978986Smsmith				sleep 1
32078986Smsmith			fi
32178986Smsmith		fi
32278986Smsmith	done
32378986Smsmith
32478986Smsmith	#
32567754Smsmith	# If user exhausted number of allowed password tries, log
32667754Smsmith	# the security event and exit immediately.
32767754Smsmith	#
32871867Smsmith	if [ $nfailures -ge $PASSWD_TRIES ]; then
32971867Smsmith		msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
33071867Smsmith		logger -p auth.notice -t sudo " " \
33171867Smsmith			"${SUDO_USER:-$USER} : $msg" \
33271867Smsmith			"; TTY=$(tty)"               \
33371867Smsmith			"; PWD=$PWD"                 \
33482367Smsmith			"; USER=root"                \
33582367Smsmith			"; COMMAND=$0"
33682367Smsmith		f_die 1 "sudo: $message"
33782367Smsmith	fi
33882367Smsmith}
33971867Smsmith
34082367Smsmith# f_mustberoot_init
34182367Smsmith#
34282367Smsmith# If not already root, make the switch to root by re-executing ourselves via
34367754Smsmith# sudo(8) using user-supplied credentials.
34467754Smsmith#
34567754Smsmith# The following environment variables effect functionality:
34667754Smsmith#
34769450Smsmith# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
34869450Smsmith# 	              that (while running as root) sudo(8) authentication is
34969450Smsmith# 	              required to proceed.
35069450Smsmith#
35167754Smsmithf_mustberoot_init()
35267754Smsmith{
35367754Smsmith	if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then
35467754Smsmith		f_become_root_via_sudo
35567754Smsmith	elif [ "$SECURE" ]; then
35667754Smsmith		f_authenticate_some_user
35767754Smsmith	fi
35867754Smsmith}
35969450Smsmith
36067754Smsmithfi # ! $_MUSTBEROOT_SUBR
36169450Smsmith