xref: /freebsd/usr.sbin/bsdconfig/usermgmt/share/group.subr (revision c66ec88fed842fbaad62c30d510644ceb7bd2d71)
1if [ ! "$_USERMGMT_GROUP_SUBR" ]; then _USERMGMT_GROUP_SUBR=1
2#
3# Copyright (c) 2012 Ron McDowell
4# Copyright (c) 2012-2014 Devin Teske
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10# 1. Redistributions of source code must retain the above copyright
11#    notice, this list of conditions and the following disclaimer.
12# 2. Redistributions in binary form must reproduce the above copyright
13#    notice, this list of conditions and the following disclaimer in the
14#    documentation and/or other materials provided with the distribution.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26# SUCH DAMAGE.
27#
28# $FreeBSD$
29#
30############################################################ INCLUDES
31
32BSDCFG_SHARE="/usr/share/bsdconfig"
33. $BSDCFG_SHARE/common.subr || exit 1
34f_dprintf "%s: loading includes..." usermgmt/group.subr
35f_include $BSDCFG_SHARE/dialog.subr
36f_include $BSDCFG_SHARE/usermgmt/group_input.subr
37
38BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="070.usermgmt"
39f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
40
41############################################################ CONFIGURATION
42
43# set some reasonable defaults if /etc/adduser.conf does not exist.
44[ -f /etc/adduser.conf ] && f_include /etc/adduser.conf
45: ${passwdtype:="yes"}
46
47############################################################ FUNCTIONS
48
49# f_group_add [$group]
50#
51# Add a group. If both $group (as a first argument) and $VAR_GROUP are unset
52# or NULL and we are running interactively, prompt the user to enter the name
53# of a new group and (if $VAR_NO_CONFIRM is unset or NULL) prompt the user to
54# answer some questions about the new group. Variables that can be used to
55# script user input:
56#
57# 	VAR_GROUP [Optional if running interactively]
58# 		The group to add. Ignored if given non-NULL first-argument.
59#	VAR_GROUP_GID [Optional]
60# 		Numerical group ID to use. If NULL or unset, the group ID is
61# 		automatically chosen.
62# 	VAR_GROUP_MEMBERS [Optional]
63# 		Comma separated list of users that are a member of this group.
64# 	VAR_GROUP_PASSWORD [Optional]
65# 		newgrp(1) password to set for the group. Default if NULL or
66# 		unset is to disable newgrp(1) password authentication.
67#
68# Returns success if the group was successfully added.
69#
70f_group_add()
71{
72	local funcname=f_group_add
73	local title # Calculated below
74	local alert=f_show_msg no_confirm=
75
76	f_getvar $VAR_NO_CONFIRM no_confirm
77	[ "$no_confirm" ] && alert=f_show_info
78
79	local input
80	f_getvar 3:-\$$VAR_GROUP input "$1"
81
82	#
83	# NB: pw(8) has a ``feature'' wherein `-n name' can be taken as GID
84	# instead of name. Work-around is to also pass `-g GID' at the same
85	# time (the GID is ignored in this case, so any GID will do).
86	#
87	if [ "$input" ] && f_quietly pw groupshow -n "$input" -g 1337; then
88		f_show_err "$msg_group_already_used" "$input"
89		return $FAILURE
90	fi
91
92	local group_name="$input"
93	while f_interactive && [ ! "$group_name" ]; do
94		f_dialog_input_group_name group_name "$group_name" ||
95			return $SUCCESS
96		[ "$group_name" ] ||
97			f_show_err "$msg_please_enter_a_group_name"
98	done
99	if [ ! "$group_name" ]; then
100		f_show_err "$msg_no_group_specified"
101		return $FAILURE
102	fi
103
104	local group_password group_gid group_members
105	f_getvar $VAR_GROUP_PASSWORD	group_password
106	f_getvar $VAR_GROUP_GID		group_gid
107	f_getvar $VAR_GROUP_MEMBERS	group_members
108
109	local group_password_disable=
110	f_interactive || [ "$group_password" ] || group_password_disable=1
111
112	if f_interactive && [ ! "$no_confirm" ]; then
113		f_dialog_noyes \
114			"$msg_use_default_values_for_all_account_details"
115		retval=$?
116		if [ $retval -eq $DIALOG_ESC ]; then
117			return $SUCCESS
118		elif [ $retval -ne $DIALOG_OK ]; then
119			#
120			# Ask series of questions to pre-fill the editor screen
121			#
122			# Defaults used in each dialog should allow the user to
123			# simply hit ENTER to proceed and cancelling a single
124			# dialog cause them to return to the previous menu.
125			#
126
127			if [ "$passwdtype" = "yes" ]; then
128				f_dialog_input_group_password group_password \
129					group_password_disable ||
130					return $FAILURE
131			fi
132			f_dialog_input_group_gid group_gid "$group_gid" ||
133				return $FAILURE
134			f_dialog_input_group_members group_members \
135				"$group_members" || return $FAILURE
136		fi
137	fi
138
139	#
140	# Loop until the user decides to Exit, Cancel, or presses ESC
141	#
142	title="$msg_add $msg_group: $group_name"
143	if f_interactive; then
144		local mtag retval defaultitem=
145		while :; do
146			f_dialog_title "$title"
147			f_dialog_menu_group_add "$defaultitem"
148			retval=$?
149			f_dialog_title_restore
150			f_dialog_menutag_fetch mtag
151			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
152			defaultitem="$mtag"
153
154			# Return if user either pressed ESC or chose Cancel/No
155			[ $retval -eq $DIALOG_OK ] || return $FAILURE
156
157			case "$mtag" in
158			X) # Add/Exit
159			   local var
160			   for var in gid members name; do
161			   	local _group_$var
162			   	eval f_shell_escape \
163			   		\"\$group_$var\" _group_$var
164			   done
165
166			   local cmd="pw groupadd -n '$_group_name'"
167			   [ "$group_gid" ] && cmd="$cmd -g '$_group_gid'"
168			   [ "$group_members" ] &&
169			   	cmd="$cmd -M '$_group_members'"
170
171			   # Execute the command (break on success)
172			   if [ "$group_password_disable" ]; then
173			   	f_eval_catch $funcname pw '%s -h -' "$cmd"
174			   elif [ "$group_password" ]; then
175			   	echo "$group_password" |
176			   		f_eval_catch $funcname \
177			   			pw '%s -h 0' "$cmd"
178			   else
179			   	f_eval_catch $funcname pw '%s' "$cmd"
180			   fi && break
181			   ;;
182			1) # Group Name (prompt for new group name)
183			   f_dialog_input_group_name input "$group_name" ||
184			   	continue
185			   if f_quietly pw groupshow -n "$input" -g 1337; then
186			   	f_show_err "$msg_group_already_used" "$input"
187			   	continue
188			   fi
189			   group_name="$input"
190			   title="$msg_add $msg_group: $group_name"
191			   ;;
192			2) # Password
193			   f_dialog_input_group_password group_password \
194			   	group_password_disable
195			   ;;
196			3) # Group ID
197			   f_dialog_input_group_gid group_gid "$group_gid"
198			   ;;
199			4) # Group Members
200			   f_dialog_input_group_members group_members \
201					"$group_members"
202			   ;;
203			esac
204		done
205	else
206		local var
207		for var in gid members name; do
208			local _group_$var
209			eval f_shell_escape \"\$group_$var\" _group_$var
210		done
211
212		# Form the command
213		local cmd="pw groupadd -n '$_group_name'"
214		[ "$group_gid" ] && cmd="$cmd -g '$_group_gid'"
215		[ "$group_members" ] && cmd="$cmd -M '$_group_members'"
216
217		# Execute the command
218		local retval err
219		if [ "$group_password_disable" ]; then
220			f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
221		elif [ "$group_password" ]; then
222			err=$( echo "$group_password" | f_eval_catch -de \
223				$funcname pw '%s -h 0' "$cmd" 2>&1 )
224		else
225			f_eval_catch -k err $funcname pw '%s' "$cmd"
226		fi
227		retval=$?
228		if [ $retval -ne $SUCCESS ]; then
229			f_show_err "%s" "$err"
230			return $retval
231		fi
232	fi
233
234	f_dialog_title "$title"
235	$alert "$msg_group_added"
236	f_dialog_title_restore
237	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
238
239	return $SUCCESS
240}
241
242# f_group_delete [$group]
243#
244# Delete a group. If both $group (as a first argument) and $VAR_GROUP are unset
245# or NULL and we are running interactively, prompt the user to select a group
246# from a list of available groups. Variables that can be used to script user
247# input:
248#
249# 	VAR_GROUP [Optional if running interactively]
250# 		The group to delete. Ignored if given non-NULL first-argument.
251#
252# Returns success if the group was successfully deleted.
253#
254f_group_delete()
255{
256	local funcname=f_group_delete
257	local title # Calculated below
258	local alert=f_show_msg no_confirm=
259
260	f_getvar $VAR_NO_CONFIRM no_confirm
261	[ "$no_confirm" ] && alert=f_show_info
262
263	local input
264	f_getvar 3:-\$$VAR_GROUP input "$1"
265
266	local group_name group_password group_gid group_members
267	if [ "$input" ] && ! f_input_group "$input"; then
268		f_show_err "$msg_group_not_found" "$input"
269		return $FAILURE
270	fi
271
272	#
273	# Loop until the user decides to Exit, Cancel, or presses ESC
274	#
275	title="$msg_delete $msg_group: $group_name"
276	if f_interactive; then
277		local mtag retval defaultitem=
278		while :; do
279        		f_dialog_title "$title"
280			f_dialog_menu_group_delete "$group_name" "$defaultitem"
281			retval=$?
282			f_dialog_title_restore
283			f_dialog_menutag_fetch mtag
284			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
285			defaultitem="$mtag"
286
287			# Return if user either pressed ESC or chose Cancel/No
288			[ $retval -eq $DIALOG_OK ] || return $FAILURE
289
290			case "$mtag" in
291			X) # Delete/Exit
292			   local _group_name
293			   f_shell_escape "$group_name" _group_name
294			   f_eval_catch $funcname pw 'pw groupdel "%s"' \
295					"$_group_name" && break
296			   ;;
297			1) # Group Name (select different group from list)
298			   f_dialog_menu_group_list "$group_name" || continue
299			   f_dialog_menutag_fetch mtag
300
301			   [ "$mtag" = "X $msg_exit" ] && continue
302
303			   if ! f_input_group "$mtag"; then
304				f_show_err "$msg_group_not_found" "$mtag"
305				# Attempt to fall back to previous selection
306				f_input_group "$input" || return $FAILURE
307			   else
308				input="$mtag"
309			   fi
310			   ;;
311			esac
312		done
313	else
314		local retval err _group_name
315		f_shell_escape "$group_name" _group_name
316		f_eval_catch -k err $funcname pw \
317			"pw groupdel '%s'" "$_group_name"
318		retval=$?
319		if [ $retval -ne $SUCCESS ]; then
320			f_show_err "%s" "$err"
321			return $retval
322		fi
323	fi
324
325        f_dialog_title "$title"
326	$alert "$msg_group_deleted"
327	f_dialog_title_restore
328	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
329
330	return $SUCCESS
331}
332
333# f_group_edit [$group]
334#
335# Modify a group. If both $group (as a first argument) and $VAR_GROUP are unset
336# or NULL and we are running interactively, prompt the user to select a group
337# from a list of available groups. Variables that can be used to script user
338# input:
339#
340# 	VAR_GROUP [Optional if running interactively]
341# 		The group to modify. Ignored if given non-NULL first-argument.
342#	VAR_GROUP_GID [Optional]
343# 		Numerical group ID to set. If NULL or unset, the group ID is
344# 		unchanged.
345# 	VAR_GROUP_MEMBERS [Optional]
346# 		Comma separated list of users that are a member of this group.
347# 		If set but NULL, group memberships are reset (no users will be
348# 		a member of this group). If unset, group membership is
349# 		unmodified.
350# 	VAR_GROUP_PASSWORD [Optional]
351# 		newgrp(1) password to set for the group. If unset, the password
352# 		is unmodified. If NULL, the newgrp(1) password is disabled.
353#
354# Returns success if the group was successfully modified.
355#
356f_group_edit()
357{
358	local funcname=f_group_edit
359	local title # Calculated below
360	local alert=f_show_msg no_confirm=
361
362	f_getvar $VAR_NO_CONFIRM no_confirm
363	[ "$no_confirm" ] && alert=f_show_info
364
365	local input
366	f_getvar 3:-\$$VAR_GROUP input "$1"
367
368	#
369	# NB: pw(8) has a ``feature'' wherein `-n name' can be taken as GID
370	# instead of name. Work-around is to also pass `-g GID' at the same
371	# time (the GID is ignored in this case, so any GID will do).
372	#
373	if [ "$input" ] && ! f_quietly pw groupshow -n "$input" -g 1337; then
374		f_show_err "$msg_group_not_found" "$input"
375		return $FAILURE
376	fi
377
378	if f_interactive && [ ! "$input" ]; then
379		f_dialog_menu_group_list || return $SUCCESS
380		f_dialog_menutag_fetch input
381		[ "$input" = "X $msg_exit" ] && return $SUCCESS
382	elif [ ! "$input" ]; then
383		f_show_err "$msg_no_group_specified"
384		return $FAILURE
385	fi
386
387	local group_name group_password group_gid group_members
388	if ! f_input_group "$input"; then
389		f_show_err "$msg_group_not_found" "$input"
390		return $FAILURE
391	fi
392
393	f_isset $VAR_GROUP_GID && f_getvar $VAR_GROUP_GID group_gid
394	local null_members=
395	if f_isset $VAR_GROUP_MEMBERS; then
396		f_getvar $VAR_GROUP_MEMBERS group_members
397		[ "$group_members" ] || null_members=1
398	fi
399	local group_password_disable=
400	if f_isset $VAR_GROUP_PASSWORD; then
401		f_getvar $VAR_GROUP_PASSWORD group_password
402		[ "$group_password" ] || group_password_disable=1
403	fi
404
405	#
406	# Loop until the user decides to Exit, Cancel, or presses ESC
407	#
408	title="$msg_edit_view $msg_group: $group_name"
409	if f_interactive; then
410		local mtag retval defaultitem=
411		while :; do
412			f_dialog_title "$title"
413			f_dialog_menu_group_edit "$defaultitem"
414			retval=$?
415			f_dialog_title_restore
416			f_dialog_menutag_fetch mtag
417			f_dprintf "retval=%u mtag=[%s]" $retval "$mtag"
418			defaultitem="$mtag"
419
420			# Return if user either pressed ESC or chose Cancel/No
421			[ $retval -eq $DIALOG_OK ] || return $FAILURE
422
423			case "$mtag" in
424			X) # Save/Exit
425			   local var
426			   for var in gid members name; do
427			   	local _group_$var
428			   	eval f_shell_escape \
429			   		\"\$group_$var\" _group_$var
430			   done
431
432			   local cmd="pw groupmod -n '$_group_name'"
433			   [ "$group_gid" ] && cmd="$cmd -g '$_group_gid'"
434			   [ "$group_members" -o "$null_members" ] &&
435			   	cmd="$cmd -M '$_group_members'"
436
437			   # Execute the command (break on success)
438			   if [ "$group_password_disable" ]; then
439			   	f_eval_catch $funcname pw '%s -h -' "$cmd"
440			   elif [ "$group_password" ]; then
441			   	echo "$group_password" | f_eval_catch \
442			   		$funcname pw '%s -h 0' "$cmd"
443			   else
444			   	f_eval_catch $funcname pw '%s' "$cmd"
445			   fi && break
446			   ;;
447			1) # Group Name (select different group from list)
448			   f_dialog_menu_group_list "$group_name" || continue
449			   f_dialog_menutag_fetch mtag
450
451			   [ "$mtag" = "X $msg_exit" ] && continue
452
453			   if ! f_input_group "$mtag"; then
454			   	f_show_err "$msg_group_not_found" "$mtag"
455			   	# Attempt to fall back to previous selection
456			   	f_input_group "$input" || return $FAILURE
457			   else
458			   	input="$mtag"
459			   fi
460			   title="$msg_edit_view $msg_group: $group_name"
461			   ;;
462			2) # Password
463			   f_dialog_input_group_password group_password \
464			   	group_password_disable
465			   ;;
466			3) # Group ID
467			   f_dialog_input_group_gid group_gid "$group_gid"
468			   ;;
469			4) # Group Members
470			   f_dialog_input_group_members group_members \
471			   	"$group_members" && [ ! "$group_members" ] &&
472			   	null_members=1
473			   ;;
474			esac
475		done
476	else
477		local var
478		for var in gid members name; do
479			local _group_$var
480			eval f_shell_escape \"\$group_$var\" _group_$var
481		done
482
483		# Form the command
484		local cmd="pw groupmod -n '$_group_name'"
485		[ "$group_gid" ] && cmd="$cmd -g '$_group_gid'"
486		[ "$group_members" -o "$null_members" ] &&
487			cmd="$cmd -M '$_group_members'"
488
489		# Execute the command
490		local retval err
491		if [ "$group_password_disable" ]; then
492			f_eval_catch -k err $funcname pw '%s -h -' "$cmd"
493		elif [ "$group_password" -o "$null_password" ]; then
494			err=$( echo "$group_password" | f_eval_catch -de \
495				$funcname pw '%s -h 0' "$cmd" 2>&1 )
496		else
497			f_eval_catch -k err $funcname pw '%s' "$cmd"
498		fi
499		retval=$?
500		if [ $retval -ne $SUCCESS ]; then
501			f_show_err "%s" "$err"
502			return $retval
503		fi
504	fi
505
506	f_dialog_title "$title"
507	$alert "$msg_group_updated"
508	f_dialog_title_restore
509	[ "$no_confirm" -a "$USE_DIALOG" ] && sleep 1
510
511	return $SUCCESS
512}
513
514############################################################ MAIN
515
516f_dprintf "%s: Successfully loaded." usermgmt/group.subr
517
518fi # ! $_USERMGMT_GROUP_SUBR
519