mustberoot.subr revision 252995
1252995Sdteskeif [ ! "$_MUSTBEROOT_SUBR" ]; then _MUSTBEROOT_SUBR=1
2252995Sdteske#
3252995Sdteske# Copyright (c) 2006-2013 Devin Teske
4252995Sdteske# All rights reserved.
5252995Sdteske#
6252995Sdteske# Redistribution and use in source and binary forms, with or without
7252995Sdteske# modification, are permitted provided that the following conditions
8252995Sdteske# are met:
9252995Sdteske# 1. Redistributions of source code must retain the above copyright
10252995Sdteske#    notice, this list of conditions and the following disclaimer.
11252995Sdteske# 2. Redistributions in binary form must reproduce the above copyright
12252995Sdteske#    notice, this list of conditions and the following disclaimer in the
13252995Sdteske#    documentation and/or other materials provided with the distribution.
14252995Sdteske#
15252995Sdteske# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16252995Sdteske# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17252995Sdteske# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18252995Sdteske# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19252995Sdteske# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20252995Sdteske# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21252995Sdteske# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22252995Sdteske# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23252995Sdteske# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24252995Sdteske# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25252995Sdteske# SUCH DAMAGE.
26252995Sdteske#
27252995Sdteske# $FreeBSD: stable/9/usr.sbin/bsdconfig/share/mustberoot.subr 252995 2013-07-07 19:13:34Z dteske $
28252995Sdteske#
29252995Sdteske############################################################ INCLUDES
30252995Sdteske
31252995SdteskeBSDCFG_SHARE="/usr/share/bsdconfig"
32252995Sdteske. $BSDCFG_SHARE/common.subr || exit 1
33252995Sdteskef_dprintf "%s: loading includes..." mustberoot.subr
34252995Sdteskef_include $BSDCFG_SHARE/dialog.subr
35252995Sdteske
36252995SdteskeBSDCFG_LIBE="/usr/libexec/bsdconfig"
37252995Sdteskef_include_lang $BSDCFG_LIBE/include/messages.subr
38252995Sdteske
39252995Sdteske############################################################ CONFIGURATION
40252995Sdteske# NOTE: These are not able to be overridden/inherited for security purposes.
41252995Sdteske
42252995Sdteske#
43252995Sdteske# Number of tries a user gets to enter his/her password before we log the
44252995Sdteske# sudo(8) failure and exit.
45252995Sdteske#
46252995SdteskePASSWD_TRIES=3
47252995Sdteske
48252995Sdteske#
49252995Sdteske# While in SECURE mode, should authentication as `root' be allowed? Set to
50252995Sdteske# non-NULL to enable authentication as `root', otherwise disabled.
51252995Sdteske#
52252995Sdteske# WARNING: 
53252995Sdteske# Unless using a custom sudo(8) configuration, user `root' should not be
54252995Sdteske# allowed because no password is required to become `root' when already `root'
55252995Sdteske# and therefore, any value entered as password will work.
56252995Sdteske#
57252995SdteskeSECURE_ALLOW_ROOT=
58252995Sdteske
59252995Sdteske#
60252995Sdteske# While in SECURE mode, should we divulge (through error message) when the
61252995Sdteske# requested authentication user does not exist? Set to non-NULL to enable,
62252995Sdteske# otherwise a non-existent user is treated like an invalid password.
63252995Sdteske#
64252995SdteskeSECURE_DIVULGE_UNKNOWN_USER=
65252995Sdteske
66252995Sdteske############################################################ FUNCTIONS
67252995Sdteske
68252995Sdteske# f_become_root_via_sudo
69252995Sdteske#
70252995Sdteske# If not running as root, prompt for sudo(8) credentials to become root.
71252995Sdteske# Re-execution of the current program via sudo is automatically handled.
72252995Sdteske#
73252995Sdteske# The following environment variables effect functionality:
74252995Sdteske#
75252995Sdteske# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
76252995Sdteske# 	              that Xdialog(1) should be used instead of dialog(1).
77252995Sdteske#
78252995Sdteskef_become_root_via_sudo()
79252995Sdteske{
80252995Sdteske	local prompt hline height width rows msg
81252995Sdteske
82252995Sdteske	[ "$( id -u )" = "0" ] && return $SUCCESS
83252995Sdteske
84252995Sdteske	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
85252995Sdteske
86252995Sdteske	#
87252995Sdteske	# Ask the user if it's OK to become root via sudo(8) and give them
88252995Sdteske	# the option to save this preference (by touch(1)ing a file in the
89252995Sdteske	# user's $HOME directory).
90252995Sdteske	#
91252995Sdteske	local checkpath="${HOME%/}/.bsdconfig_uses_sudo"
92252995Sdteske	if [ ! -e "$checkpath" ]; then
93252995Sdteske		prompt=$( printf "$msg_you_are_not_root_but" bsdconfig )
94252995Sdteske		msg=$( printf "$msg_always_try_sudo_when_run_as" "$USER" )
95252995Sdteske		local menu_list="
96252995Sdteske			'X' '$msg_cancel_exit'
97252995Sdteske			'1' '$msg'
98252995Sdteske			'2' '$msg_try_sudo_only_this_once'
99252995Sdteske		" # END-QUOTE
100252995Sdteske		hline="$hline_arrows_tab_enter"
101252995Sdteske
102252995Sdteske		eval f_dialog_menu_size height width rows \
103252995Sdteske		                        \"\$DIALOG_TITLE\"     \
104252995Sdteske		                        \"\$DIALOG_BACKTITLE\" \
105252995Sdteske		                        \"\$prompt\"           \
106252995Sdteske		                        \"\$hline\"            \
107252995Sdteske		                        $menu_list
108252995Sdteske
109252995Sdteske		local mtag
110252995Sdteske		mtag=$( eval $DIALOG \
111252995Sdteske			--title \"\$DIALOG_TITLE\"         \
112252995Sdteske			--backtitle \"\$DIALOG_BACKTITLE\" \
113252995Sdteske			--hline \"\$hline\"                \
114252995Sdteske			--ok-label \"\$msg_ok\"            \
115252995Sdteske			--cancel-label \"\$msg_cancel\"    \
116252995Sdteske			--menu \"\$prompt\"                \
117252995Sdteske			$height $width $rows               \
118252995Sdteske			$menu_list                         \
119252995Sdteske			2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
120252995Sdteske		) || f_die
121252995Sdteske		f_dialog_data_sanitize mtag
122252995Sdteske
123252995Sdteske		case "$mtag" in
124252995Sdteske		X) # Cancel/Exit
125252995Sdteske		   f_die ;;
126252995Sdteske		1) # Always try sudo(8) when run as $user
127252995Sdteske			local err
128252995Sdteske			if ! err=$( touch "$checkpath" 2>&1 ); then
129252995Sdteske				f_dialog_msgbox "$err"
130252995Sdteske			else
131252995Sdteske				f_show_msg "$msg_created_path" "$checkpath"
132252995Sdteske			fi
133252995Sdteske		esac
134252995Sdteske	else
135252995Sdteske		#
136252995Sdteske		# This user has created the path signing-off on sudo(8)-use
137252995Sdteske		# but let's still give them a short/quick/unobtrusive reminder
138252995Sdteske		#
139252995Sdteske		f_dialog_info "$msg_becoming_root_via_sudo"
140252995Sdteske		[ "$USE_XDIALOG" ] || sleep 0.6
141252995Sdteske	fi
142252995Sdteske
143252995Sdteske	#
144252995Sdteske	# Check sudo(8) access before prompting for password.
145252995Sdteske	#
146252995Sdteske	:| sudo -S -v 2> /dev/null
147252995Sdteske	if [ $? -ne $SUCCESS ]; then
148252995Sdteske		#
149252995Sdteske		# sudo(8) access denied. Prompt for their password.
150252995Sdteske		#
151252995Sdteske		prompt="$msg_please_enter_password"
152252995Sdteske		hline="$hline_alnum_punc_tab_enter"
153252995Sdteske		f_dialog_inputbox_size height width \
154252995Sdteske		                       "$DIALOG_TITLE"     \
155252995Sdteske		                       "$DIALOG_BACKTITLE" \
156252995Sdteske		                       "$prompt"           \
157252995Sdteske		                       "$hline"
158252995Sdteske
159252995Sdteske		#
160252995Sdteske		# Continue prompting until they either Cancel, succeed
161252995Sdteske		# or exceed the number of allowed failures.
162252995Sdteske		#
163252995Sdteske		local password nfailures=0 retval
164252995Sdteske		while [ $nfailures -lt $PASSWD_TRIES ]; do
165252995Sdteske			if [ "$USE_XDIALOG" ]; then
166252995Sdteske				password=$( $DIALOG \
167252995Sdteske					--title "$DIALOG_TITLE"         \
168252995Sdteske					--backtitle "$DIALOG_BACKTITLE" \
169252995Sdteske					--hline "$hline"                \
170252995Sdteske					--ok-label "$msg_ok"            \
171252995Sdteske					--cancel-label "$msg_cancel"    \
172252995Sdteske					--password --inputbox "$prompt" \
173252995Sdteske					$height $width                  \
174252995Sdteske					2>&1 > /dev/null
175252995Sdteske				)
176252995Sdteske				retval=$?
177252995Sdteske
178252995Sdteske				# Catch X11-related errors
179252995Sdteske				if [ $retval -eq 255 ]; then
180252995Sdteske					f_die $retval "$password"
181252995Sdteske				elif [ $retval -ne 0 ]; then
182252995Sdteske					# User cancelled
183252995Sdteske					exit $retval
184252995Sdteske				fi
185252995Sdteske			else
186252995Sdteske				password=$( $DIALOG \
187252995Sdteske					--title "$DIALOG_TITLE"         \
188252995Sdteske					--backtitle "$DIALOG_BACKTITLE" \
189252995Sdteske					--hline "$hline"                \
190252995Sdteske					--ok-label "$msg_ok"            \
191252995Sdteske					--cancel-label "$msg_cancel"    \
192252995Sdteske					--insecure                      \
193252995Sdteske					--passwordbox "$prompt"         \
194252995Sdteske					$height $width                  \
195252995Sdteske					2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD
196252995Sdteske				) || exit $?
197252995Sdteske			fi
198252995Sdteske			debug= f_dialog_line_sanitize password
199252995Sdteske
200252995Sdteske			#
201252995Sdteske			# Validate sudo(8) credentials
202252995Sdteske			#
203252995Sdteske			sudo -S -v 2> /dev/null <<-EOF
204252995Sdteske			$password
205252995Sdteske			EOF
206252995Sdteske			retval=$?
207252995Sdteske			unset password # scrub memory
208252995Sdteske			if [ $retval -eq $SUCCESS ]; then
209252995Sdteske				# Access granted...
210252995Sdteske				break
211252995Sdteske			else
212252995Sdteske				# Access denied...
213252995Sdteske				nfailures=$(( $nfailures + 1 ))
214252995Sdteske
215252995Sdteske				# introduce a short delay
216252995Sdteske				if [ $nfailures -lt $PASSWD_TRIES ]; then
217252995Sdteske					f_dialog_info "$msg_sorry_try_again"
218252995Sdteske					sleep 1
219252995Sdteske				fi
220252995Sdteske			fi
221252995Sdteske		done
222252995Sdteske
223252995Sdteske		#
224252995Sdteske		# If user exhausted number of allowed password tries, log
225252995Sdteske		# the security event and exit immediately.
226252995Sdteske		#
227252995Sdteske		if [ $nfailures -ge $PASSWD_TRIES ]; then
228252995Sdteske			msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
229252995Sdteske			logger -p auth.notice -t sudo " " \
230252995Sdteske				"$USER : $msg" \
231252995Sdteske				"; TTY=$(tty)" \
232252995Sdteske				"; PWD=$PWD"   \
233252995Sdteske				"; USER=root"  \
234252995Sdteske				"; COMMAND=$0"
235252995Sdteske			f_die 1 "sudo: $msg"
236252995Sdteske		fi
237252995Sdteske	fi
238252995Sdteske
239252995Sdteske	# Use xauth(1) to grant root the ability to use this X11/SSH session
240252995Sdteske	if [ "$USE_XDIALOG" -a "$SSH_CONNECTION" -a "$DISPLAY" ]; then
241252995Sdteske		f_have xauth || f_die 1 \
242252995Sdteske			"$msg_no_such_file_or_directory" "$pgm" "xauth"
243252995Sdteske		local HOSTNAME displaynum
244252995Sdteske		HOSTNAME=$(hostname)
245252995Sdteske		displaynum="${DISPLAY#*:}"
246252995Sdteske		xauth -f ~/.Xauthority extract - $HOSTNAME/unix:$displaynum \
247252995Sdteske			$HOSTNAME:$displaynum | sudo sh -c 'xauth -ivf \
248252995Sdteske			~root/.Xauthority merge - > /dev/null 2>&1'
249252995Sdteske	fi
250252995Sdteske
251252995Sdteske	# Re-execute ourselves with sudo(8)
252252995Sdteske	f_dprintf "%s: Becoming root via sudo(8)..." mustberoot.subr
253252995Sdteske	if [ $ARGC -gt 0 ]; then
254252995Sdteske		exec sudo "$0" $ARGV
255252995Sdteske	else
256252995Sdteske		exec sudo "$0"
257252995Sdteske	fi
258252995Sdteske	exit $? # Never reached unless error
259252995Sdteske}
260252995Sdteske
261252995Sdteske# f_authenticate_some_user
262252995Sdteske#
263252995Sdteske# Only used if running as root and requires X11 (see USE_XDIALOG below).
264252995Sdteske# Prompts the user to enter a username and password to be authenticated via
265252995Sdteske# sudo(8) to proceed.
266252995Sdteske#
267252995Sdteske# The following environment variables effect functionality:
268252995Sdteske#
269252995Sdteske# 	USE_XDIALOG   Either NULL or Non-NULL. If given a value will indicate
270252995Sdteske# 	              that Xdialog(1) should be used instead of dialog(1).
271252995Sdteske#
272252995Sdteskef_authenticate_some_user()
273252995Sdteske{
274252995Sdteske	local msg hline height width
275252995Sdteske
276252995Sdteske	f_have sudo || f_die 1 "$msg_must_be_root_to_execute" "$pgm"
277252995Sdteske
278252995Sdteske	#
279252995Sdteske	# Secure-mode has been requested.
280252995Sdteske	#
281252995Sdteske
282252995Sdteske	[ "$USE_XDIALOG" ] || f_die 1 "$msg_secure_mode_requires_x11"
283252995Sdteske	[ "$(id -u)" = "0" ] || f_die 1 "$msg_secure_mode_requires_root"
284252995Sdteske
285252995Sdteske	#
286252995Sdteske	# Prompt for sudo(8) credentials.
287252995Sdteske	#
288252995Sdteske
289252995Sdteske	msg="$msg_please_enter_username_password"
290252995Sdteske	hline="$hline_alnum_punc_tab_enter"
291252995Sdteske	f_xdialog_2inputsbox_size height width \
292252995Sdteske	                          "$DIALOG_TITLE"      \
293252995Sdteske	                          "$DIALOG_BACKTITLE"  \
294252995Sdteske	                          "$msg"               \
295252995Sdteske	                          "$field_username" "" \
296252995Sdteske	                          "$field_password" ""
297252995Sdteske	height=$(( $height + 2 )) # Add height for --password
298252995Sdteske
299252995Sdteske	#
300252995Sdteske	# Continue prompting until they either Cancel, succeed or exceed the
301252995Sdteske	# number of allowed failures.
302252995Sdteske	#
303252995Sdteske	local user_pass nfailures=0 retval
304252995Sdteske	while [ $nfailures -lt $PASSWD_TRIES ]; do
305252995Sdteske		user_pass=$( $DIALOG \
306252995Sdteske			--title "$DIALOG_TITLE"         \
307252995Sdteske			--backtitle "$DIALOG_BACKTITLE" \
308252995Sdteske			--hline "$hline"                \
309252995Sdteske			--ok-label "$msg_ok"            \
310252995Sdteske			--cancel-label "$msg_cancel"    \
311252995Sdteske			--password --2inputsbox "$msg"  \
312252995Sdteske			$height $width                  \
313252995Sdteske			"$field_username" ""            \
314252995Sdteske			"$field_password" ""            \
315252995Sdteske			2>&1 > /dev/null )
316252995Sdteske		retval=$?
317252995Sdteske
318252995Sdteske		# Catch X11-related errors
319252995Sdteske		[ $retval -eq 255 ] && f_die $retval "$user_pass"
320252995Sdteske
321252995Sdteske		# Exit if the user cancelled.
322252995Sdteske		[ $retval -eq $SUCCESS ] || exit $retval
323252995Sdteske
324252995Sdteske		#
325252995Sdteske		# Make sure the user exists and is non-root
326252995Sdteske		#
327252995Sdteske		local user password
328252995Sdteske		user="${user_pass%%/*}"
329252995Sdteske		password="${user_pass#*/}"
330252995Sdteske		unset user_pass # scrub memory
331252995Sdteske		if [ ! "$user" ]; then
332252995Sdteske			nfailures=$(( $nfailures + 1 ))
333252995Sdteske			f_show_msg "$msg_no_username"
334252995Sdteske			continue
335252995Sdteske		fi
336252995Sdteske		if [ ! "$SECURE_ALLOW_ROOT" ]; then
337252995Sdteske			case "$user" in
338252995Sdteske			root|toor)
339252995Sdteske				nfailures=$(( $nfailures + 1 ))
340252995Sdteske				f_show_msg "$msg_user_disallowed" "$user"
341252995Sdteske				continue
342252995Sdteske			esac
343252995Sdteske		fi
344252995Sdteske		if ! f_quietly id "$user"; then
345252995Sdteske			nfailures=$(( $nfailures + 1 ))
346252995Sdteske			if [ "$SECURE_DIVULGE_UNKNOWN_USER" ]; then
347252995Sdteske				f_show_msg "$msg_unknown_user" "$user"
348252995Sdteske			elif [ $nfailures -lt $PASSWD_TRIES ]; then
349252995Sdteske				f_dialog_info "$msg_sorry_try_again"
350252995Sdteske				sleep 1
351252995Sdteske			fi
352252995Sdteske			continue
353252995Sdteske		fi
354252995Sdteske
355252995Sdteske		#
356252995Sdteske		# Validate sudo(8) credentials for given user
357252995Sdteske		#
358252995Sdteske		su -m "$user" <<-EOF
359252995Sdteske			sh <<EOS
360252995Sdteske				sudo -k
361252995Sdteske				sudo -S -v 2> /dev/null <<EOP
362252995Sdteske				$password
363252995Sdteske				EOP
364252995Sdteske			EOS
365252995Sdteske		EOF
366252995Sdteske		retval=$?
367252995Sdteske		unset user
368252995Sdteske		unset password # scrub memory
369252995Sdteske
370252995Sdteske		if [ $retval -eq $SUCCESS ]; then
371252995Sdteske			# Access granted...
372252995Sdteske			break
373252995Sdteske		else
374252995Sdteske			# Access denied...
375252995Sdteske			nfailures=$(( $nfailures + 1 ))
376252995Sdteske
377252995Sdteske			# introduce a short delay
378252995Sdteske			if [ $nfailures -lt $PASSWD_TRIES ]; then
379252995Sdteske				f_dialog_info "$msg_sorry_try_again"
380252995Sdteske				sleep 1
381252995Sdteske			fi
382252995Sdteske		fi
383252995Sdteske	done
384252995Sdteske
385252995Sdteske	#
386252995Sdteske	# If user exhausted number of allowed password tries, log
387252995Sdteske	# the security event and exit immediately.
388252995Sdteske	#
389252995Sdteske	if [ $nfailures -ge $PASSWD_TRIES ]; then
390252995Sdteske		msg=$( printf "$msg_nfailed_attempts" "$nfailures" )
391252995Sdteske		logger -p auth.notice -t sudo " " \
392252995Sdteske			"${SUDO_USER:-$USER} : $msg" \
393252995Sdteske			"; TTY=$(tty)"               \
394252995Sdteske			"; PWD=$PWD"                 \
395252995Sdteske			"; USER=root"                \
396252995Sdteske			"; COMMAND=$0"
397252995Sdteske		f_die 1 "sudo: $message"
398252995Sdteske	fi
399252995Sdteske}
400252995Sdteske
401252995Sdteske# f_mustberoot_init
402252995Sdteske#
403252995Sdteske# If not already root, make the switch to root by re-executing ourselves via
404252995Sdteske# sudo(8) using user-supplied credentials.
405252995Sdteske#
406252995Sdteske# The following environment variables effect functionality:
407252995Sdteske#
408252995Sdteske# 	SECURE        Either NULL or Non-NULL. If given a value will indicate
409252995Sdteske# 	              that (while running as root) sudo(8) authentication is
410252995Sdteske# 	              required to proceed.
411252995Sdteske#
412252995Sdteskef_mustberoot_init()
413252995Sdteske{
414252995Sdteske	if [ "$(id -u)" != "0" -a ! "$SECURE" ]; then
415252995Sdteske		f_become_root_via_sudo
416252995Sdteske	elif [ "$SECURE" ]; then
417252995Sdteske		f_authenticate_some_user
418252995Sdteske	fi
419252995Sdteske}
420252995Sdteske
421252995Sdteske############################################################ MAIN
422252995Sdteske
423252995Sdteskef_dprintf "%s: Successfully loaded." mustberoot.subr
424252995Sdteske
425252995Sdteskefi # ! $_MUSTBEROOT_SUBR
426