mustberoot.subr revision 251236
1227825Stheravenif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
2227825Stheraven#
3227825Stheraven# Copyright (c) 2006-2013 Devin Teske
4227825Stheraven# All Rights Reserved.
5227825Stheraven#
6227825Stheraven# Redistribution and use in source and binary forms, with or without
7227825Stheraven# modification, are permitted provided that the following conditions
8227825Stheraven# are met:
9227825Stheraven# 1. Redistributions of source code must retain the above copyright
10227825Stheraven#    notice, this list of conditions and the following disclaimer.
11227825Stheraven# 2. Redistributions in binary form must reproduce the above copyright
12227825Stheraven#    notice, this list of conditions and the following disclaimer in the
13227825Stheraven#    documentation and/or other materials provided with the distribution.
14227825Stheraven#
15227825Stheraven# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16227825Stheraven# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, THE
17227825Stheraven# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18227825Stheraven# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19227825Stheraven# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20227825Stheraven# DAMAGES (INLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21227825Stheraven# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22227825Stheraven# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23227825Stheraven# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24227825Stheraven# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25227825Stheraven# SUCH DAMAGE.
26227825Stheraven#
27227825Stheraven# $FreeBSD: head/usr.sbin/bsdconfig/share/mustberoot.subr 251236 2013-06-01 23:58:44Z dteske $
28227825Stheraven#
29227825Stheraven############################################################ INCLUDES
30227825Stheraven
31227825StheravenBSDCFG_SHARE="/usr/share/bsdconfig"
32227825Stheraven. $BSDCFG_SHARE/common.subr || exit 1
33227825Stheravenf_dprintf "%s: loading includes..." mustberoot.subr
34227825Stheravenf_include $BSDCFG_SHARE/dialog.subr
35227825Stheraven
36227825StheravenBSDCFG_LIBE="/usr/libexec/bsdconfig"
37227825Stheravenf_include_lang $BSDCFG_LIBE/include/messages.subr
38227825Stheraven
39227825Stheraven############################################################ CONFIGURATION
40227825Stheraven# NOTE: These are not able to be overridden/inherited for security purposes.
41227825Stheraven
42227825Stheraven#
43227825Stheraven# Number of tries a user gets to enter his/her password before we log the
44227825Stheraven# sudo(8) failure and exit.
45227825Stheraven#
46227825StheravenPASSWD_TRIES=3
47227825Stheraven
48227825Stheraven#
49227825Stheraven# While in SECURE mode, should authentication as `root' be allowed? Set to
50227825Stheraven# non-NULL to enable authentication as `root', otherwise disabled.
51227825Stheraven#
52227825Stheraven# WARNING: 
53227825Stheraven# Unless using a custom sudo(8) configuration, user `root' should not be
54227825Stheraven# allowed because no password is required to become `root' when already `root'
55227825Stheraven# and therefore, any value entered as password will work.
56227825Stheraven#
57227825StheravenSECURE_ALLOW_ROOT=
58227825Stheraven
59227825Stheraven#
60227825Stheraven# While in SECURE mode, should we divulge (through error message) when the
61227825Stheraven# requested authentication user does not exist? Set to non-NULL to enable,
62227825Stheraven# otherwise a non-existent user is treated like an invalid password.
63227825Stheraven#
64227825StheravenSECURE_DIVULGE_UNKNOWN_USER=
65227825Stheraven
66227825Stheraven############################################################ FUNCTIONS
67227825Stheraven
68227825Stheraven# f_become_root_via_sudo
69227825Stheraven#
70227825Stheraven# If not running as root, prompt for sudo(8) credentials to become root.
71227825Stheraven# Re-execution of the current program via sudo is automatically handled.
72227825Stheraven#
73227825Stheraven# The following environment variables effect functionality:
74227825Stheraven#
75227825Stheraven# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
76227825Stheraven# 	              that Xdialog(1) should be used instead of dialog(1).
77227825Stheraven#
78227825Stheravenf_become_root_via_sudo()
79227825Stheraven{
80227825Stheraven	local msg hline height width rows
81227825Stheraven
82227825Stheraven	[ "$( id -u )" = "0" ] && return $SUCCESS
83227825Stheraven
84227825Stheraven	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
85227825Stheraven
86227825Stheraven	#
87227825Stheraven	# Ask the user if it's OK to become root via sudo(8) and give them
88227825Stheraven	# the option to save this preference (by touch(1)ing a file in the
89227825Stheraven	# user's $HOME directory).
90227825Stheraven	#
91227825Stheraven	local checkpath="${HOME%/}/.bsdconfig_uses_sudo"
92227825Stheraven	if [ ! -e "$checkpath" ]; then
93227825Stheraven		msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" )
94227825Stheraven		local menu_list="
95227825Stheraven			'X' '$msg_cancel_exit'
96227825Stheraven			'1' '$msg'
97227825Stheraven			'2' '$msg_try_sudo_only_this_once'
98227825Stheraven		" # END-QUOTE
99227825Stheraven		msg=$( printf "$msg_you_are_not_root_but" bsdconfig )
100227825Stheraven		hline="$hline_arrows_tab_enter"
101227825Stheraven		eval f_dialog_menu_size height width rows \
102227825Stheraven		                        \"\$DIALOG_TITLE\"     \
103227825Stheraven		                        \"\$DIALOG_BACKTITLE\" \
104227825Stheraven		                        \"\$msg\"              \
105227825Stheraven		                        \"\$hline\"            \
106227825Stheraven		                        $menu_list
107227825Stheraven
108227825Stheraven		local mtag
109227825Stheraven		mtag=$( eval $DIALOG \
110227825Stheraven			--title \"\$DIALOG_TITLE\"         \
111227825Stheraven			--backtitle \"\$DIALOG_BACKTITLE\" \
112227825Stheraven			--hline \"\$hline\"                \
113227825Stheraven			--ok-label \"\$msg_ok\"            \
114227825Stheraven			--cancel-label \"\$msg_cancel\"    \
115227825Stheraven			--menu \"\$msg\"                   \
116227825Stheraven			$height $width $rows               \
117227825Stheraven			$menu_list                         \
118227825Stheraven			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
119227825Stheraven		) || f_die
120227825Stheraven		f_dialog_data_sanitize mtag
121227825Stheraven
122227825Stheraven		case "$mtag" in
123227825Stheraven		X) # Cancel/Exit
124227825Stheraven		   f_die ;;
125227825Stheraven		1) # Always try sudo(8) when run as $user
126227825Stheraven			local err
127227825Stheraven			if ! err=$( touch "$checkpath" 2>&1 ); then
128227825Stheraven				f_dialog_msgbox "$err"
129227825Stheraven			else
130227825Stheraven				f_show_msg "$msg_created_path" "$checkpath"
131227825Stheraven			fi
132227825Stheraven		esac
133227825Stheraven	else
134227825Stheraven		#
135227825Stheraven		# This user has created the path signing-off on sudo(8)-use
136227825Stheraven		# but let's still give them a short/quick/unobtrusive reminder
137227825Stheraven		#
138227825Stheraven		f_dialog_info "$msg_becoming_root_via_sudo"
139227825Stheraven		[ "$USE_XDIALOG" ] || sleep 0.6
140227825Stheraven	fi
141227825Stheraven
142227825Stheraven	#
143227825Stheraven	# Check sudo(8) access before prompting for password.
144227825Stheraven	#
145227825Stheraven	:| sudo -S -v 2> /dev/null
146227825Stheraven	if [ $? -ne $SUCCESS ]; then
147227825Stheraven		#
148227825Stheraven		# sudo(8) access denied. Prompt for their password.
149227825Stheraven		#
150227825Stheraven		msg="$msg_please_enter_password"
151227825Stheraven		hline="$hline_alnum_punc_tab_enter"
152227825Stheraven		f_dialog_inputbox_size height width \
153227825Stheraven		                       "$DIALOG_TITLE"     \
154227825Stheraven		                       "$DIALOG_BACKTITLE" \
155227825Stheraven		                       "$msg"              \
156227825Stheraven		                       "$hline"
157227825Stheraven
158227825Stheraven		#
159227825Stheraven		# Continue prompting until they either Cancel, succeed
160227825Stheraven		# or exceed the number of allowed failures.
161227825Stheraven		#
162227825Stheraven		local password nfailures=0 retval
163227825Stheraven		while [ $nfailures -lt $PASSWD_TRIES ]; do
164227825Stheraven			if [ "$USE_XDIALOG" ]; then
165227825Stheraven				password=$( $DIALOG \
166227825Stheraven					--title "$DIALOG_TITLE"         \
167227825Stheraven					--backtitle "$DIALOG_BACKTITLE" \
168227825Stheraven					--hline "$hline"                \
169227825Stheraven					--ok-label "$msg_ok"            \
170227825Stheraven					--cancel-label "$msg_cancel"    \
171227825Stheraven					--password --inputbox "$msg"    \
172227825Stheraven					$height $width                  \
173227825Stheraven					2>&1 > /dev/null )
174227825Stheraven				retval=$?
175227825Stheraven
176227825Stheraven				# Catch X11-related errors
177227825Stheraven				[ $retval -eq 255 ] &&
178227825Stheraven					f_die $retval "$password"
179227825Stheraven			else
180227825Stheraven				local dialog_inputbox
181227825Stheraven				dialog_inputbox=$( $DIALOG \
182227825Stheraven					--title "$DIALOG_TITLE"         \
183227825Stheraven					--backtitle "$DIALOG_BACKTITLE" \
184227825Stheraven					--hline "$hline"                \
185227825Stheraven					--ok-label "$msg_ok"            \
186227825Stheraven					--cancel-label "$msg_cancel"    \
187227825Stheraven					--insecure                      \
188227825Stheraven					--passwordbox "$msg"            \
189227825Stheraven					$height $width                  \
190227825Stheraven					2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
191227825Stheraven				)
192227825Stheraven				retval=$?
193227825Stheraven				setvar DIALOG_INPUTBOX_$$ "$dialog_inputbox"
194227825Stheraven				password=$( f_dialog_inputstr )
195227825Stheraven			fi
196227825Stheraven
197227825Stheraven			# Exit if the user cancelled.
198227825Stheraven			[ $retval -eq $SUCCESS ] || exit $retval
199227825Stheraven
200227825Stheraven			#
201227825Stheraven			# Validate sudo(8) credentials
202227825Stheraven			#
203227825Stheraven			sudo -S -v 2> /dev/null <<-EOF
204227825Stheraven			$password
205227825Stheraven			EOF
206227825Stheraven			retval=$?
207227825Stheraven			unset password # scrub memory
208227825Stheraven			if [ $retval -eq $SUCCESS ]; then
209227825Stheraven				# Access granted...
210227825Stheraven				break
211227825Stheraven			else
212227825Stheraven				# Access denied...
213227825Stheraven				nfailures=$(( $nfailures + 1 ))
214227825Stheraven
215227825Stheraven				# introduce a short delay
216227825Stheraven				if [ $nfailures -lt $PASSWD_TRIES ]; then
217227825Stheraven					f_dialog_info "$msg_sorry_try_again"
218227825Stheraven					sleep 1
219227825Stheraven				fi
220227825Stheraven			fi
221227825Stheraven		done
222227825Stheraven
223227825Stheraven		#
224227825Stheraven		# If user exhausted number of allowed password tries, log
225227825Stheraven		# the security event and exit immediately.
226227825Stheraven		#
227227825Stheraven		if [ $nfailures -ge $PASSWD_TRIES ]; then
228227825Stheraven			msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
229227825Stheraven			logger -p auth.notice -t sudo " " \
230227825Stheraven				"$USER : $msg" \
231227825Stheraven				"; TTY=$(tty)" \
232227825Stheraven				"; PWD=$PWD"   \
233227825Stheraven				"; USER=root"  \
234227825Stheraven				"; COMMAND=$0"
235227825Stheraven			f_die 1 "sudo: $msg"
236227825Stheraven		fi
237227825Stheraven	fi
238227825Stheraven
239227825Stheraven	# Use xauth(1) to grant root the ability to use this X11/SSH session
240227825Stheraven	if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then
241227825Stheraven		f_have xauth || f_die 1 \
242227825Stheraven			"$msg_no_such_file_or_directory" "$pgm" "xauth"
243227825Stheraven		local HOSTNAME displaynum
244227825Stheraven		HOSTNAME=$(hostname)
245227825Stheraven		displaynum="${DISPLAY#*:}"
246227825Stheraven		xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \
247227825Stheraven			$HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \
248227825Stheraven			~root/.Xauthority merge - > /dev/null 2>&1'
249227825Stheraven	fi
250227825Stheraven
251227825Stheraven	# Re-execute ourselves with sudo(8)
252227825Stheraven	f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr
253227825Stheraven	if [ $ARGC -gt 0 ]; then
254227825Stheraven		exec sudo "$0" $ARGV
255227825Stheraven	else
256227825Stheraven		exec sudo "$0"
257227825Stheraven	fi
258227825Stheraven	exit $? # Never reached unless error
259227825Stheraven}
260227825Stheraven
261227825Stheraven# f_authenticate_some_user
262227825Stheraven#
263227825Stheraven# Only used if running as root and requires X11 (see USE_XDIALOG below).
264227825Stheraven# Prompts the user to enter a username and password to be authenticated via
265227825Stheraven# sudo(8) to proceed.
266227825Stheraven#
267227825Stheraven# The following environment variables effect functionality:
268227825Stheraven#
269227825Stheraven# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
270227825Stheraven# 	              that Xdialog(1) should be used instead of dialog(1).
271227825Stheraven#
272227825Stheravenf_authenticate_some_user()
273227825Stheraven{
274227825Stheraven	local msg hline height width
275227825Stheraven
276227825Stheraven	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
277227825Stheraven
278227825Stheraven	#
279227825Stheraven	# Secure-mode has been requested.
280227825Stheraven	#
281227825Stheraven
282227825Stheraven	[ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11"
283227825Stheraven	[ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root"
284227825Stheraven
285227825Stheraven	#
286227825Stheraven	# Prompt for sudo(8) credentials.
287227825Stheraven	#
288227825Stheraven
289227825Stheraven	msg="$msg_please_enter_username_password"
290227825Stheraven	hline="$hline_alnum_punc_tab_enter"
291227825Stheraven	f_xdialog_2inputsbox_size height width \
292227825Stheraven	                          "$DIALOG_TITLE"      \
293227825Stheraven	                          "$DIALOG_BACKTITLE"  \
294227825Stheraven	                          "$msg"               \
295227825Stheraven	                          "$field_username" "" \
296227825Stheraven	                          "$field_password" ""
297227825Stheraven	height=$(( $height + 2 )) # Add height for --password
298227825Stheraven
299227825Stheraven	#
300227825Stheraven	# Continue prompting until they either Cancel, succeed or exceed the
301227825Stheraven	# number of allowed failures.
302227825Stheraven	#
303227825Stheraven	local user_pass nfailures=0 retval
304227825Stheraven	while [ $nfailures -lt $PASSWD_TRIES ]; do
305227825Stheraven		user_pass=$( $DIALOG \
306227825Stheraven			--title "$DIALOG_TITLE"         \
307227825Stheraven			--backtitle "$DIALOG_BACKTITLE" \
308227825Stheraven			--hline "$hline"                \
309227825Stheraven			--ok-label "$msg_ok"            \
310227825Stheraven			--cancel-label "$msg_cancel"    \
311227825Stheraven			--password --2inputsbox "$msg"  \
312227825Stheraven			$height $width                  \
313227825Stheraven			"$field_username" ""            \
314227825Stheraven			"$field_password" ""            \
315227825Stheraven			2>&1 > /dev/null )
316227825Stheraven		retval=$?
317227825Stheraven
318227825Stheraven		# Catch X11-related errors
319227825Stheraven		[ $retval -eq 255 ] && f_die $retval "$user_pass"
320227825Stheraven
321227825Stheraven		# Exit if the user cancelled.
322227825Stheraven		[ $retval -eq $SUCCESS ] || exit $retval
323227825Stheraven
324227825Stheraven		#
325227825Stheraven		# Make sure the user exists and is non-root
326227825Stheraven		#
327227825Stheraven		local user password
328227825Stheraven		user="${user_pass%%/*}"
329227825Stheraven		password="${user_pass#*/}"
330227825Stheraven		unset user_pass # scrub memory
331227825Stheraven		if [ ! "$user" ]; then
332227825Stheraven			nfailures=$(( $nfailures + 1 ))
333227825Stheraven			f_dialog_msgbox "$msg_no_username"
334227825Stheraven			continue
335227825Stheraven		fi
336227825Stheraven		if [ ! "$SECURE_ALLOW_ROOT" ]; then
337227825Stheraven			case "$user" in
338227825Stheraven			root|toor)
339227825Stheraven				nfailures=$(( $nfailures + 1 ))
340227825Stheraven				f_show_msg "$msg_user_disallowed" "$user"
341227825Stheraven				continue
342227825Stheraven			esac
343227825Stheraven		fi
344227825Stheraven		if ! f_quietly id "$user"; then
345227825Stheraven			nfailures=$(( $nfailures + 1 ))
346227825Stheraven			if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
347227825Stheraven				f_show_msg "$msg_unknown_user" "$user"
348227825Stheraven			elif [ $nfailures -lt $PASSWD_TRIES ]; then
349227825Stheraven				f_dialog_info "$msg_sorry_try_again"
350227825Stheraven				sleep 1
351227825Stheraven			fi
352227825Stheraven			continue
353227825Stheraven		fi
354227825Stheraven
355227825Stheraven		#
356227825Stheraven		# Validate sudo(8) credentials for given user
357227825Stheraven		#
358227825Stheraven		su -m "$user" <<-EOF
359227825Stheraven			sh <<EOS
360227825Stheraven				sudo -k
361227825Stheraven				sudo -S -v 2> /dev/null <<EOP
362227825Stheraven				$password
363227825Stheraven				EOP
364227825Stheraven			EOS
365227825Stheraven		EOF
366227825Stheraven		retval=$?
367227825Stheraven		unset user
368227825Stheraven		unset password # scrub memory
369227825Stheraven
370227825Stheraven		if [ $retval -eq $SUCCESS ]; then
371227825Stheraven			# Access granted...
372227825Stheraven			break
373227825Stheraven		else
374227825Stheraven			# Access denied...
375227825Stheraven			nfailures=$(( $nfailures + 1 ))
376227825Stheraven
377227825Stheraven			# introduce a short delay
378227825Stheraven			if [ $nfailures -lt $PASSWD_TRIES ]; then
379227825Stheraven				f_dialog_info "$msg_sorry_try_again"
380227825Stheraven				sleep 1
381227825Stheraven			fi
382227825Stheraven		fi
383227825Stheraven	done
384227825Stheraven
385227825Stheraven	#
386227825Stheraven	# If user exhausted number of allowed password tries, log
387227825Stheraven	# the security event and exit immediately.
388227825Stheraven	#
389227825Stheraven	if [ $nfailures -ge $PASSWD_TRIES ]; then
390227825Stheraven		msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
391227825Stheraven		logger -p auth.notice -t sudo " " \
392227825Stheraven			"${SUDO_USER:-$USER} : $msg" \
393227825Stheraven			"; TTY=$(tty)"               \
394227825Stheraven			"; PWD=$PWD"                 \
395227825Stheraven			"; USER=root"                \
396227825Stheraven			"; COMMAND=$0"
397227825Stheraven		f_die 1 "sudo: $message"
398227825Stheraven	fi
399227825Stheraven}
400227825Stheraven
401227825Stheraven# f_mustberoot_init
402227825Stheraven#
403227825Stheraven# If not already root, make the switch to root by re-executing ourselves via
404227825Stheraven# sudo(8) using user-supplied credentials.
405227825Stheraven#
406227825Stheraven# The following environment variables effect functionality:
407227825Stheraven#
408227825Stheraven# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
409227825Stheraven# 	              that (while running as root) sudo(8) authentication is
410227825Stheraven# 	              required to proceed.
411227825Stheraven#
412227825Stheravenf_mustberoot_init()
413227825Stheraven{
414227825Stheraven	if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then
415227825Stheraven		f_become_root_via_sudo
416227825Stheraven	elif [ "$SECURE" ]; then
417227825Stheraven		f_authenticate_some_user
418227825Stheraven	fi
419227825Stheraven}
420227825Stheraven
421227825Stheraven############################################################ MAIN
422227825Stheraven
423227825Stheravenf_dprintf "%s: Successfully loaded." mustberoot.subr
424227825Stheraven
425227825Stheravenfi # ! $_MUSTBEROOT_SUBR
426227825Stheraven