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