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