mustberoot.subr revision 238438
1238438Sdteskeif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
2238438Sdteske#
3238438Sdteske# Copyright (c) 2006-2012 Devin Teske
4238438Sdteske# All Rights Reserved.
5238438Sdteske#
6238438Sdteske# Redistribution and use in source and binary forms, with or without
7238438Sdteske# modification, are permitted provided that the following conditions
8238438Sdteske# are met:
9238438Sdteske# 1. Redistributions of source code must retain the above copyright
10238438Sdteske#    notice, this list of conditions and the following disclaimer.
11238438Sdteske# 2. Redistributions in binary form must reproduce the above copyright
12238438Sdteske#    notice, this list of conditions and the following disclaimer in the
13238438Sdteske#    documentation and/or other materials provided with the distribution.
14238438Sdteske#
15238438Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16238438Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17238438Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18238438Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19238438Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20238438Sdteske# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21238438Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22238438Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23238438Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24238438Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25238438Sdteske# SUCH DAMAGE.
26238438Sdteske#
27238438Sdteske# $FreeBSD: head/usr.sbin/bsdconfig/include/mustberoot.subr 238438 2012-07-14 03:16:57Z dteske $
28238438Sdteske#
29238438Sdteske############################################################ INCLUDES
30238438Sdteske
31238438SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig"
32238438Sdteske. $BSDCFG_LIBE/include/common.subr || exit 1
33238438Sdteskef_include $BSDCFG_LIBE/include/dialog.subr
34238438Sdteskef_include_lang $BSDCFG_LIBE/include/messages.subr
35238438Sdteske
36238438Sdteske############################################################ CONFIGURATION
37238438Sdteske# NOTE: These are not able to be overridden/inherited for security purposes.
38238438Sdteske
39238438Sdteske#
40238438Sdteske# Number of tries a user gets to enter his/her password before we log the
41238438Sdteske# sudo(8) failure and exit.
42238438Sdteske#
43238438SdteskePASSWD_TRIES=3
44238438Sdteske
45238438Sdteske#
46238438Sdteske# While in SECURE mode, should authentication as `root' be allowed? Set to
47238438Sdteske# non-NULL to enable authentication as `root', otherwise disabled.
48238438Sdteske#
49238438Sdteske# WARNING: 
50238438Sdteske# Unless using a custom sudo(8) configuration, user `root' should not be
51238438Sdteske# allowed because no password is required to become `root' when already `root'
52238438Sdteske# and therefore, any value entered as password will work.
53238438Sdteske#
54238438SdteskeSECURE_ALLOW_ROOT=
55238438Sdteske
56238438Sdteske#
57238438Sdteske# While in SECURE mode, should we divulge (through error message) when the
58238438Sdteske# requested authentication user does not exist? Set to non-NULL to enable,
59238438Sdteske# otherwise a non-existent user is treated like an invalid password.
60238438Sdteske#
61238438SdteskeSECURE_DIVULGE_UNKNOWN_USER=
62238438Sdteske
63238438Sdteske############################################################ FUNCTIONS
64238438Sdteske
65238438Sdteske# f_become_root_via_sudo
66238438Sdteske#
67238438Sdteske# If not running as root, prompt for sudo(8) credentials to become root.
68238438Sdteske# Re-execution of the current program via sudo is automatically handled.
69238438Sdteske#
70238438Sdteske# The following environment variables effect functionality:
71238438Sdteske#
72238438Sdteske# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
73238438Sdteske# 	              that Xdialog(1) should be used instead of dialog(1).
74238438Sdteske#
75238438Sdteskef_become_root_via_sudo()
76238438Sdteske{
77238438Sdteske	local msg hline size
78238438Sdteske
79238438Sdteske	[ "$( id -u )" = "0" ] && return $SUCCESS
80238438Sdteske
81238438Sdteske	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
82238438Sdteske
83238438Sdteske	#
84238438Sdteske	# Check sudo(8) access before prompting for password.
85238438Sdteske	#
86238438Sdteske	:| sudo -S -v 2> /dev/null
87238438Sdteske	if [ $? -ne $SUCCESS ]; then
88238438Sdteske		#
89238438Sdteske		# sudo(8) access denied. Prompt for their password.
90238438Sdteske		#
91238438Sdteske		msg="$msg_please_enter_password"
92238438Sdteske		hline="$hline_alnum_punc_tab_enter"
93238438Sdteske		size=$( f_dialog_inputbox_size \
94238438Sdteske		        	"$DIALOG_TITLE"     \
95238438Sdteske		        	"$DIALOG_BACKTITLE" \
96238438Sdteske		        	"$msg"              \
97238438Sdteske		        	"$hline"            )
98238438Sdteske
99238438Sdteske		#
100238438Sdteske		# Continue prompting until they either Cancel, succeed
101238438Sdteske		# or exceed the number of allowed failures.
102238438Sdteske		#
103238438Sdteske		local password nfailures=0 retval
104238438Sdteske		while [ $nfailures -lt $PASSWD_TRIES ]; do
105238438Sdteske			if [ "$USE_XDIALOG" ]; then
106238438Sdteske				password=$( $DIALOG \
107238438Sdteske					--title "$DIALOG_TITLE"            \
108238438Sdteske					--backtitle "$DIALOG_BACKTITLE"    \
109238438Sdteske					--hline "$hline"                   \
110238438Sdteske					--ok-label "$msg_ok"               \
111238438Sdteske					--cancel-label "$msg_cancel"       \
112238438Sdteske					--password --inputbox "$msg" $size \
113238438Sdteske					2>&1 > /dev/null )
114238438Sdteske				retval=$?
115238438Sdteske
116238438Sdteske				# Catch X11-related errors
117238438Sdteske				[ $retval -eq 255 ] &&
118238438Sdteske					f_die $retval "$password"
119238438Sdteske			else
120238438Sdteske				$DIALOG \
121238438Sdteske					--title "$DIALOG_TITLE"         \
122238438Sdteske					--backtitle "$DIALOG_BACKTITLE" \
123238438Sdteske					--hline "$hline"                \
124238438Sdteske					--ok-label "$msg_ok"            \
125238438Sdteske					--cancel-label "$msg_cancel"    \
126238438Sdteske					--insecure                      \
127238438Sdteske					--passwordbox "$msg" $size      \
128238438Sdteske					2> "$DIALOG_TMPDIR/dialog.inputbox.$$"
129238438Sdteske				retval=$?
130238438Sdteske				password=$( f_dialog_inputstr )
131238438Sdteske			fi
132238438Sdteske
133238438Sdteske			# Exit if the user cancelled.
134238438Sdteske			[ $retval -eq $SUCCESS ] || exit $retval
135238438Sdteske
136238438Sdteske			#
137238438Sdteske			# Validate sudo(8) credentials
138238438Sdteske			#
139238438Sdteske			sudo -S -v 2> /dev/null <<-EOF
140238438Sdteske			$password
141238438Sdteske			EOF
142238438Sdteske			retval=$?
143238438Sdteske			unset password # scrub memory
144238438Sdteske			if [ $retval -eq $SUCCESS ]; then
145238438Sdteske				# Access granted...
146238438Sdteske				break
147238438Sdteske			else
148238438Sdteske				# Access denied...
149238438Sdteske				nfailures=$(( $nfailures + 1 ))
150238438Sdteske
151238438Sdteske				# introduce a short delay
152238438Sdteske				if [ $nfailures -lt $PASSWD_TRIES ]; then
153238438Sdteske					f_dialog_info "$msg_sorry_try_again"
154238438Sdteske					sleep 1
155238438Sdteske				fi
156238438Sdteske			fi
157238438Sdteske		done
158238438Sdteske
159238438Sdteske		#
160238438Sdteske		# If user exhausted number of allowed password tries, log
161238438Sdteske		# the security event and exit immediately.
162238438Sdteske		#
163238438Sdteske		if [ $nfailures -ge $PASSWD_TRIES ]; then
164238438Sdteske			msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
165238438Sdteske			logger -p auth.notice -t sudo " " \
166238438Sdteske				"$USER : $msg" \
167238438Sdteske				"; TTY=$(tty)" \
168238438Sdteske				"; PWD=$PWD"   \
169238438Sdteske				"; USER=root"  \
170238438Sdteske				"; COMMAND=$0"
171238438Sdteske			f_die 1 "sudo: $msg"
172238438Sdteske		fi
173238438Sdteske	fi
174238438Sdteske
175238438Sdteske	# Use xauth(1) to grant root the ability to use this X11/SSH session
176238438Sdteske	if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then
177238438Sdteske		f_have xauth || f_die 1 \
178238438Sdteske			"$msg_no_such_file_or_directory" "$pgm" "xauth"
179238438Sdteske		local HOSTNAME displaynum
180238438Sdteske		HOSTNAME=$(hostname)
181238438Sdteske		displaynum="${DISPLAY#*:}"
182238438Sdteske		xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \
183238438Sdteske			$HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \
184238438Sdteske			~root/.Xauthority merge - > /dev/null 2>&1'
185238438Sdteske	fi
186238438Sdteske
187238438Sdteske	# Re-execute ourselves with sudo(8)
188238438Sdteske	if [ $ARGC -gt 0 ]; then
189238438Sdteske		exec sudo "$0" $ARGV
190238438Sdteske	else
191238438Sdteske		exec sudo "$0"
192238438Sdteske	fi
193238438Sdteske	exit $? # Never reached unless error
194238438Sdteske}
195238438Sdteske
196238438Sdteske# f_authenticate_some_user
197238438Sdteske#
198238438Sdteske# Only used if running as root and requires X11 (see USE_XDIALOG below).
199238438Sdteske# Prompts the user to enter a username and password to be authenticated via
200238438Sdteske# sudo(8) to proceed.
201238438Sdteske#
202238438Sdteske# The following environment variables effect functionality:
203238438Sdteske#
204238438Sdteske# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
205238438Sdteske# 	              that Xdialog(1) should be used instead of dialog(1).
206238438Sdteske#
207238438Sdteskef_authenticate_some_user()
208238438Sdteske{
209238438Sdteske	local msg hline size width height
210238438Sdteske
211238438Sdteske	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
212238438Sdteske
213238438Sdteske	#
214238438Sdteske	# Secure-mode has been requested.
215238438Sdteske	#
216238438Sdteske 
217238438Sdteske	[ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11"
218238438Sdteske	[ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root"
219238438Sdteske
220238438Sdteske	#
221238438Sdteske	# Prompt for sudo(8) credentials.
222238438Sdteske	#
223238438Sdteske
224238438Sdteske	msg="$msg_please_enter_username_password"
225238438Sdteske	hline="$hline_alnum_punc_tab_enter"
226238438Sdteske	size=$( f_xdialog_2inputsbox_size \
227238438Sdteske	        	"$DIALOG_TITLE"      \
228238438Sdteske	        	"$DIALOG_BACKTITLE"  \
229238438Sdteske	        	"$msg"               \
230238438Sdteske	        	"$field_username" "" \
231238438Sdteske	        	"$field_password" "" )
232238438Sdteske	width="${size##*[$IFS]}"
233238438Sdteske	height="${size%%[$IFS]*}"
234238438Sdteske	height=$(( $height + 2 )) # Add height for --password
235238438Sdteske
236238438Sdteske	#
237238438Sdteske	# Continue prompting until they either Cancel, succeed or exceed the
238238438Sdteske	# number of allowed failures.
239238438Sdteske	#
240238438Sdteske	local user_pass nfailures=0 retval
241238438Sdteske	while [ $nfailures -lt $PASSWD_TRIES ]; do
242238438Sdteske		user_pass=$( $DIALOG \
243238438Sdteske			--title "$DIALOG_TITLE"         \
244238438Sdteske			--backtitle "$DIALOG_BACKTITLE" \
245238438Sdteske			--hline "$hline"                \
246238438Sdteske			--ok-label "$msg_ok"            \
247238438Sdteske			--cancel-label "$msg_cancel"    \
248238438Sdteske			--password --2inputsbox "$msg"  \
249238438Sdteske			$height $width                  \
250238438Sdteske			"$field_username" ""            \
251238438Sdteske			"$field_password" ""            \
252238438Sdteske			2>&1 > /dev/null )
253238438Sdteske		retval=$?
254238438Sdteske
255238438Sdteske		# Catch X11-related errors
256238438Sdteske		[ $retval -eq 255 ] && f_die $retval "$user_pass"
257238438Sdteske
258238438Sdteske		# Exit if the user cancelled.
259238438Sdteske		[ $retval -eq $SUCCESS ] || exit $retval
260238438Sdteske
261238438Sdteske		#
262238438Sdteske		# Make sure the user exists and is non-root
263238438Sdteske		#
264238438Sdteske		local user password
265238438Sdteske		user="${user_pass%%/*}"
266238438Sdteske		password="${user_pass#*/}"
267238438Sdteske		unset user_pass # scrub memory
268238438Sdteske		if [ ! "$user" ]; then
269238438Sdteske			nfailures=$(( $nfailures + 1 ))
270238438Sdteske			f_dialog_msgbox "$msg_no_username"
271238438Sdteske			continue
272238438Sdteske		fi
273238438Sdteske		if [ ! "$SECURE_ALLOW_ROOT" ]; then
274238438Sdteske			case "$user" in
275238438Sdteske			root|toor)
276238438Sdteske				nfailures=$(( $nfailures + 1 ))
277238438Sdteske				f_dialog_msgbox "$( printf \
278238438Sdteske					"$msg_user_disallowed" "$user" )"
279238438Sdteske				continue
280238438Sdteske			esac
281238438Sdteske		fi
282238438Sdteske		if ! f_quietly id "$user"; then
283238438Sdteske			nfailures=$(( $nfailures + 1 ))
284238438Sdteske			if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
285238438Sdteske				f_dialog_msgbox "$( printf \
286238438Sdteske					"$msg_unknown_user" "$user" )"
287238438Sdteske			elif [ $nfailures -lt $PASSWD_TRIES ]; then
288238438Sdteske				f_dialog_info "$msg_sorry_try_again"
289238438Sdteske				sleep 1
290238438Sdteske			fi
291238438Sdteske			continue
292238438Sdteske		fi
293238438Sdteske
294238438Sdteske		#
295238438Sdteske		# Validate sudo(8) credentials for given user
296238438Sdteske		#
297238438Sdteske		su -m "$user" <<-EOF
298238438Sdteske			sh <<EOS
299238438Sdteske				sudo -k
300238438Sdteske				sudo -S -v 2> /dev/null <<EOP
301238438Sdteske				$password
302238438Sdteske				EOP
303238438Sdteske			EOS
304238438Sdteske		EOF
305238438Sdteske		retval=$?
306238438Sdteske		unset user
307238438Sdteske		unset password # scrub memory
308238438Sdteske
309238438Sdteske		if [ $retval -eq $SUCCESS ]; then
310238438Sdteske			# Access granted...
311238438Sdteske			break
312238438Sdteske		else
313238438Sdteske			# Access denied...
314238438Sdteske			nfailures=$(( $nfailures + 1 ))
315238438Sdteske
316238438Sdteske			# introduce a short delay
317238438Sdteske			if [ $nfailures -lt $PASSWD_TRIES ]; then
318238438Sdteske				f_dialog_info "$msg_sorry_try_again"
319238438Sdteske				sleep 1
320238438Sdteske			fi
321238438Sdteske		fi
322238438Sdteske	done
323238438Sdteske
324238438Sdteske	#
325238438Sdteske	# If user exhausted number of allowed password tries, log
326238438Sdteske	# the security event and exit immediately.
327238438Sdteske	#
328238438Sdteske	if [ $nfailures -ge $PASSWD_TRIES ]; then
329238438Sdteske		msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
330238438Sdteske		logger -p auth.notice -t sudo " " \
331238438Sdteske			"${SUDO_USER:-$USER} : $msg" \
332238438Sdteske			"; TTY=$(tty)"               \
333238438Sdteske			"; PWD=$PWD"                 \
334238438Sdteske			"; USER=root"                \
335238438Sdteske			"; COMMAND=$0"
336238438Sdteske		f_die 1 "sudo: $message"
337238438Sdteske	fi
338238438Sdteske}
339238438Sdteske
340238438Sdteske# f_mustberoot_init
341238438Sdteske#
342238438Sdteske# If not already root, make the switch to root by re-executing ourselves via
343238438Sdteske# sudo(8) using user-supplied credentials.
344238438Sdteske#
345238438Sdteske# The following environment variables effect functionality:
346238438Sdteske#
347238438Sdteske# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
348238438Sdteske# 	              that (while running as root) sudo(8) authentication is
349238438Sdteske# 	              required to proceed.
350238438Sdteske#
351238438Sdteskef_mustberoot_init()
352238438Sdteske{
353238438Sdteske	if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then
354238438Sdteske		f_become_root_via_sudo
355238438Sdteske	elif [ "$SECURE" ]; then
356238438Sdteske		f_authenticate_some_user
357238438Sdteske	fi
358238438Sdteske}
359238438Sdteske
360238438Sdteskefi # ! $_MUSTBEROOT_SUBR
361