xref: /freebsd/usr.sbin/adduser/adduser.sh (revision 0b39b2e2ddb2df1d1325e042893ddcb1a1c12b8e)
17cdfce09SScott Long#!/bin/sh
27cdfce09SScott Long#
34d846d26SWarner Losh# SPDX-License-Identifier: BSD-2-Clause
41de7b4b8SPedro F. Giffuni#
50d640c0dSMike Makonnen# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved.
67cdfce09SScott Long#
77cdfce09SScott Long# Redistribution and use in source and binary forms, with or without
87cdfce09SScott Long# modification, are permitted provided that the following conditions
97cdfce09SScott Long# are met:
107cdfce09SScott Long# 1. Redistributions of source code must retain the above copyright
117cdfce09SScott Long#    notice, this list of conditions and the following disclaimer.
127cdfce09SScott Long# 2. Redistributions in binary form must reproduce the above copyright
137cdfce09SScott Long#    notice, this list of conditions and the following disclaimer in the
147cdfce09SScott Long#    documentation and/or other materials provided with the distribution.
157cdfce09SScott Long#
167cdfce09SScott Long# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177cdfce09SScott Long# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
187cdfce09SScott Long# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
197cdfce09SScott Long# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
207cdfce09SScott Long# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
217cdfce09SScott Long# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
227cdfce09SScott Long# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
237cdfce09SScott Long# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
247cdfce09SScott Long# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
257cdfce09SScott Long# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
267cdfce09SScott Long#
2722884fddSMike Makonnen#       Email: Mike Makonnen <mtm@FreeBSD.Org>
287cdfce09SScott Long#
297cdfce09SScott Long#
307cdfce09SScott Long
317cdfce09SScott Long# err msg
327cdfce09SScott Long#	Display $msg on stderr, unless we're being quiet.
337cdfce09SScott Long#
347cdfce09SScott Longerr() {
357cdfce09SScott Long	if [ -z "$quietflag" ]; then
367cdfce09SScott Long		echo 1>&2 ${THISCMD}: ERROR: $*
377cdfce09SScott Long	fi
387cdfce09SScott Long}
397cdfce09SScott Long
407cdfce09SScott Long# info msg
417cdfce09SScott Long#	Display $msg on stdout, unless we're being quiet.
427cdfce09SScott Long#
437cdfce09SScott Longinfo() {
447cdfce09SScott Long	if [ -z "$quietflag" ]; then
457cdfce09SScott Long		echo ${THISCMD}: INFO: $*
467cdfce09SScott Long	fi
477cdfce09SScott Long}
487cdfce09SScott Long
497cdfce09SScott Long# get_nextuid
507cdfce09SScott Long#	Output the value of $_uid if it is available for use. If it
517cdfce09SScott Long#	is not, output the value of the next higher uid that is available.
527cdfce09SScott Long#	If a uid is not specified, output the first available uid, as indicated
537cdfce09SScott Long#	by pw(8).
547cdfce09SScott Long#
557cdfce09SScott Longget_nextuid () {
5646a619c6SDag-Erling Smørgrav	local _uid=$1 _nextuid=
577cdfce09SScott Long
587cdfce09SScott Long	if [ -z "$_uid" ]; then
59170d0882SDag-Erling Smørgrav		_nextuid="$(${PWCMD} usernext | cut -f1 -d:)"
607cdfce09SScott Long	else
617cdfce09SScott Long		while : ; do
62b3733389SDag-Erling Smørgrav			if ! ${PWCMD} usershow $_uid > /dev/null 2>&1; then
637cdfce09SScott Long				_nextuid=$_uid
647cdfce09SScott Long				break
657cdfce09SScott Long			fi
66b3733389SDag-Erling Smørgrav			_uid=$((_uid + 1))
677cdfce09SScott Long		done
687cdfce09SScott Long	fi
697cdfce09SScott Long	echo $_nextuid
707cdfce09SScott Long}
717cdfce09SScott Long
727cdfce09SScott Long# show_usage
737cdfce09SScott Long#	Display usage information for this utility.
747cdfce09SScott Long#
757cdfce09SScott Longshow_usage() {
767cdfce09SScott Long	echo "usage: ${THISCMD} [options]"
777cdfce09SScott Long	echo "  options may include:"
787cdfce09SScott Long	echo "  -C		save to the configuration file only"
790d640c0dSMike Makonnen	echo "  -D		do not attempt to create the home directory"
807cdfce09SScott Long	echo "  -E		disable this account after creation"
817cdfce09SScott Long	echo "  -G		additional groups to add accounts to"
827cdfce09SScott Long	echo "  -L		login class of the user"
8343cb08ceSMike Makonnen	echo "  -M		file permission for home directory"
847cdfce09SScott Long	echo "  -N		do not read configuration file"
85215c0a51SJohn Grafton	echo "  -Z		do not attempt to create ZFS home dataset"
860d640c0dSMike Makonnen	echo "  -S		a nonexistent shell is not an error"
877cdfce09SScott Long	echo "  -d		home directory"
887cdfce09SScott Long	echo "  -f		file from which input will be received"
89e33b8d97SMike Makonnen	echo "  -g		default login group"
907cdfce09SScott Long	echo "  -h		display this usage message"
917cdfce09SScott Long	echo "  -k		path to skeleton home directory"
927cdfce09SScott Long	echo "  -m		user welcome message file"
937cdfce09SScott Long	echo "  -q		absolute minimal user feedback"
947cdfce09SScott Long	echo "  -s		shell"
957cdfce09SScott Long	echo "  -u		uid to start at"
967cdfce09SScott Long	echo "  -w		password type: no, none, yes or random"
977cdfce09SScott Long}
987cdfce09SScott Long
997cdfce09SScott Long# valid_shells
1007cdfce09SScott Long#	Outputs a list of valid shells from /etc/shells. Only the
1017cdfce09SScott Long#	basename of the shell is output.
1027cdfce09SScott Long#
1037cdfce09SScott Longvalid_shells() {
10446a619c6SDag-Erling Smørgrav	local _prefix=
105170d0882SDag-Erling Smørgrav
106170d0882SDag-Erling Smørgrav	${GREPCMD} '^[^#]' ${ETCSHELLS} |
1077cdfce09SScott Long	while read _path _junk ; do
108170d0882SDag-Erling Smørgrav		echo -n "${_prefix}${_path##*/}"
1097cdfce09SScott Long		_prefix=' '
1107cdfce09SScott Long	done
1118923e98bSMike Makonnen
11268050033SColin Percival	# /usr/sbin/nologin is a special case
1138923e98bSMike Makonnen	[ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}"
1147cdfce09SScott Long}
1157cdfce09SScott Long
1167cdfce09SScott Long# fullpath_from_shell shell
117e7291a6bSMike Makonnen#	Given $shell, which is either the full path to a shell or
118e7291a6bSMike Makonnen#	the basename component of a valid shell, get the
1197cdfce09SScott Long#	full path to the shell from the /etc/shells file.
1207cdfce09SScott Long#
1217cdfce09SScott Longfullpath_from_shell() {
12246a619c6SDag-Erling Smørgrav	local _shell=$1 _fullpath=
123170d0882SDag-Erling Smørgrav
124170d0882SDag-Erling Smørgrav	if [ -z "$_shell" ]; then
125170d0882SDag-Erling Smørgrav		return
126170d0882SDag-Erling Smørgrav	fi
1277cdfce09SScott Long
128ce655e04SPeter Pentchev	# /usr/sbin/nologin is a special case; it needs to be handled
129170d0882SDag-Erling Smørgrav	# before the grep | while loop, since a 'return' from within
130ce655e04SPeter Pentchev	# a subshell will not terminate the function's execution, and
131ce655e04SPeter Pentchev	# the path to the nologin shell might be printed out twice.
132ce655e04SPeter Pentchev	#
133170d0882SDag-Erling Smørgrav	if [ "$_shell" = "${NOLOGIN}" ] ||
134170d0882SDag-Erling Smørgrav	    [ "$_shell" = "${NOLOGIN_PATH}" ]; then
135ce655e04SPeter Pentchev		echo ${NOLOGIN_PATH}
136170d0882SDag-Erling Smørgrav		return
137ce655e04SPeter Pentchev	fi
138ce655e04SPeter Pentchev
139170d0882SDag-Erling Smørgrav	${GREPCMD} '^[^#]' ${ETCSHELLS} |
1407cdfce09SScott Long	while read _path _junk ; do
141170d0882SDag-Erling Smørgrav		if [ "$_path" = "$_shell" ] ||
142170d0882SDag-Erling Smørgrav		    [ "${_path##*/}" = "$_shell" ]; then
143170d0882SDag-Erling Smørgrav			echo "$_path"
144170d0882SDag-Erling Smørgrav			break
1457cdfce09SScott Long		fi
1467cdfce09SScott Long	done
1477cdfce09SScott Long}
1487cdfce09SScott Long
1498923e98bSMike Makonnen# shell_exists shell
1508923e98bSMike Makonnen#	If the given shell is listed in ${ETCSHELLS} or it is
1518923e98bSMike Makonnen#	the nologin shell this function will return 0.
1528923e98bSMike Makonnen#	Otherwise, it will return 1. If shell is valid but
1538923e98bSMike Makonnen#	the path is invalid or it is not executable it
1548923e98bSMike Makonnen#	will emit an informational message saying so.
1558923e98bSMike Makonnen#
15605c9bdf5SJoel Dahlshell_exists() {
15746a619c6SDag-Erling Smørgrav	local _sh=$1
1588923e98bSMike Makonnen
159170d0882SDag-Erling Smørgrav	if [ -z "$(fullpath_from_shell "$_sh")" ] ; then
1608923e98bSMike Makonnen		err "Invalid shell ($_sh) for user $username."
1618923e98bSMike Makonnen		return 1
1628923e98bSMike Makonnen	fi
163170d0882SDag-Erling Smørgrav	[ -x "$_sh" ] ||
1640e08168eSMike Makonnen	    info "The shell ($_sh) does not exist or is not executable."
1658923e98bSMike Makonnen	return 0
1668923e98bSMike Makonnen}
1678923e98bSMike Makonnen
1687cdfce09SScott Long# save_config
1697cdfce09SScott Long#	Save some variables to a configuration file.
1707cdfce09SScott Long#	Note: not all script variables are saved, only those that
1717cdfce09SScott Long#	      it makes sense to save.
1727cdfce09SScott Long#
1737cdfce09SScott Longsave_config() {
1747cdfce09SScott Long	echo "# Configuration file for adduser(8)."     >  ${ADDUSERCONF}
1757cdfce09SScott Long	echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF}
176170d0882SDag-Erling Smørgrav	echo "# Last Modified on $(${DATECMD})."	>> ${ADDUSERCONF}
1777cdfce09SScott Long	echo ''				>> ${ADDUSERCONF}
17843cb08ceSMike Makonnen	echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF}
179642a7af5SMike Makonnen	echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF}
1807cdfce09SScott Long	echo "defaultclass=$uclass"	>> ${ADDUSERCONF}
1817cdfce09SScott Long	echo "defaultgroups=$ugroups"	>> ${ADDUSERCONF}
1827cdfce09SScott Long	echo "passwdtype=$passwdtype" 	>> ${ADDUSERCONF}
1837cdfce09SScott Long	echo "homeprefix=$homeprefix" 	>> ${ADDUSERCONF}
1847cdfce09SScott Long	echo "defaultshell=$ushell"	>> ${ADDUSERCONF}
1857cdfce09SScott Long	echo "udotdir=$udotdir"		>> ${ADDUSERCONF}
1867cdfce09SScott Long	echo "msgfile=$msgfile"		>> ${ADDUSERCONF}
1877cdfce09SScott Long	echo "disableflag=$disableflag" >> ${ADDUSERCONF}
188ac3cd471SMike Makonnen	echo "uidstart=$uidstart"       >> ${ADDUSERCONF}
1897cdfce09SScott Long}
1907cdfce09SScott Long
1917cdfce09SScott Long# add_user
1927cdfce09SScott Long#	Add a user to the user database. If the user chose to send a welcome
1937cdfce09SScott Long#	message or lock the account, do so.
1947cdfce09SScott Long#
1957cdfce09SScott Longadd_user() {
19646a619c6SDag-Erling Smørgrav	local _uid= _name= _comment= _gecos= _home= _group= _grouplist=
19746a619c6SDag-Erling Smørgrav	local _shell= _class= _dotdir= _expire= _pwexpire= _passwd= _upasswd=
19846a619c6SDag-Erling Smørgrav	local _passwdmethod= _pwcmd=
1997cdfce09SScott Long
2007cdfce09SScott Long	# Is this a configuration run? If so, don't modify user database.
2017cdfce09SScott Long	#
2027cdfce09SScott Long	if [ -n "$configflag" ]; then
2037cdfce09SScott Long		save_config
2047cdfce09SScott Long		return
2057cdfce09SScott Long	fi
2067cdfce09SScott Long
207c8336599SMike Makonnen	_name="-n '$username'"
208c95c14d4SMike Makonnen	[ -n "$uuid" ] && _uid='-u "$uuid"'
209c95c14d4SMike Makonnen	[ -n "$ulogingroup" ] && _group='-g "$ulogingroup"'
210c95c14d4SMike Makonnen	[ -n "$ugroups" ] && _grouplist='-G "$ugroups"'
211c95c14d4SMike Makonnen	[ -n "$ushell" ] && _shell='-s "$ushell"'
212c95c14d4SMike Makonnen	[ -n "$uclass" ] && _class='-L "$uclass"'
213c95c14d4SMike Makonnen	[ -n "$ugecos" ] && _comment='-c "$ugecos"'
214c95c14d4SMike Makonnen	[ -n "$udotdir" ] && _dotdir='-k "$udotdir"'
215c95c14d4SMike Makonnen	[ -n "$uexpire" ] && _expire='-e "$uexpire"'
216c95c14d4SMike Makonnen	[ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"'
217170d0882SDag-Erling Smørgrav	if [ -z "$Dflag" ] && [ -n "$uhome" ]; then
2180d640c0dSMike Makonnen		# The /nonexistent home directory is special. It
2190d640c0dSMike Makonnen		# means the user has no home directory.
2200d640c0dSMike Makonnen		if [ "$uhome" = "$NOHOME" ]; then
2210d640c0dSMike Makonnen			_home='-d "$uhome"'
2220d640c0dSMike Makonnen		else
22343cb08ceSMike Makonnen			# Use home directory permissions if specified
22443cb08ceSMike Makonnen			if [ -n "$uhomeperm" ]; then
22543cb08ceSMike Makonnen				_home='-m -d "$uhome" -M "$uhomeperm"'
22643cb08ceSMike Makonnen			else
2270d640c0dSMike Makonnen				_home='-m -d "$uhome"'
2280d640c0dSMike Makonnen			fi
22943cb08ceSMike Makonnen		fi
230170d0882SDag-Erling Smørgrav	elif [ -n "$Dflag" ] && [ -n "$uhome" ]; then
2310d640c0dSMike Makonnen		_home='-d "$uhome"'
2320d640c0dSMike Makonnen	fi
2337cdfce09SScott Long	case $passwdtype in
2347cdfce09SScott Long	no)
2357cdfce09SScott Long		_passwdmethod="-w no"
2367cdfce09SScott Long		_passwd="-h -"
2377cdfce09SScott Long		;;
2387cdfce09SScott Long	yes)
239c95c14d4SMike Makonnen		# Note on processing the password: The outer double quotes
240c95c14d4SMike Makonnen		# make literal everything except ` and \ and $.
241c95c14d4SMike Makonnen		# The outer single quotes make literal ` and $.
242c95c14d4SMike Makonnen		# We can ensure the \ isn't treated specially by specifying
243c95c14d4SMike Makonnen		# the -r switch to the read command used to obtain the input.
244c95c14d4SMike Makonnen		#
2457cdfce09SScott Long		_passwdmethod="-w yes"
2467cdfce09SScott Long		_passwd="-h 0"
247c95c14d4SMike Makonnen		_upasswd='echo "$upass" |'
2487cdfce09SScott Long		;;
2497cdfce09SScott Long	none)
2507cdfce09SScott Long		_passwdmethod="-w none"
2517cdfce09SScott Long		;;
2527cdfce09SScott Long	random)
2537cdfce09SScott Long		_passwdmethod="-w random"
2547cdfce09SScott Long		;;
2557cdfce09SScott Long	esac
2567cdfce09SScott Long
257215c0a51SJohn Grafton	# create ZFS dataset before home directory is created with pw
258215c0a51SJohn Grafton	if [ "${Zcreate}" = "yes" ]; then
259215c0a51SJohn Grafton		if [ "${Zencrypt}" = "yes" ]; then
260215c0a51SJohn Grafton			echo "Enter encryption keyphrase for ZFS dataset (${zhome}):"
261215c0a51SJohn Grafton		fi
262215c0a51SJohn Grafton		if [ -n "$BSDINSTALL_CHROOT" ]; then
263215c0a51SJohn Grafton			create_zfs_chrooted_dataset
264215c0a51SJohn Grafton		else
265b3733389SDag-Erling Smørgrav			if ! create_zfs_dataset; then
266215c0a51SJohn Grafton				err "There was an error adding user ($username)."
267215c0a51SJohn Grafton				return 1
268215c0a51SJohn Grafton			fi
269215c0a51SJohn Grafton		fi
270215c0a51SJohn Grafton	fi
271215c0a51SJohn Grafton
2727cdfce09SScott Long	_pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment"
2737cdfce09SScott Long	_pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"
2747cdfce09SScott Long	_pwcmd="$_pwcmd $_expire $_pwexpire"
2757cdfce09SScott Long
276170d0882SDag-Erling Smørgrav	if ! _output=$(eval $_pwcmd) ; then
2777cdfce09SScott Long		err "There was an error adding user ($username)."
2787cdfce09SScott Long		return 1
2797cdfce09SScott Long	else
2807cdfce09SScott Long		info "Successfully added ($username) to the user database."
2817cdfce09SScott Long		if [ "random" = "$passwdtype" ]; then
2827cdfce09SScott Long			randompass="$_output"
2837cdfce09SScott Long			info "Password for ($username) is: $randompass"
2847cdfce09SScott Long		fi
2857cdfce09SScott Long	fi
2867cdfce09SScott Long
2877cdfce09SScott Long	if [ -n "$disableflag" ]; then
2887cdfce09SScott Long		if ${PWCMD} lock $username ; then
2897cdfce09SScott Long			info "Account ($username) is locked."
2907cdfce09SScott Long		else
2917cdfce09SScott Long			info "Account ($username) could NOT be locked."
2927cdfce09SScott Long		fi
2937cdfce09SScott Long	fi
2947cdfce09SScott Long
295215c0a51SJohn Grafton	# give newly created user permissions to their home zfs dataset
296215c0a51SJohn Grafton	if [ "${Zcreate}" = "yes" ]; then
297215c0a51SJohn Grafton		set_zfs_perms
298215c0a51SJohn Grafton		if [ -n "$BSDINSTALL_CHROOT" ]; then
299215c0a51SJohn Grafton			umount_legacy_zfs
300215c0a51SJohn Grafton		fi
301215c0a51SJohn Grafton	fi
302215c0a51SJohn Grafton
30346a619c6SDag-Erling Smørgrav	local _line= _owner= _perms= _file= _dir=
3047cdfce09SScott Long	if [ -n "$msgflag" ]; then
305170d0882SDag-Erling Smørgrav		if [ -r "$msgfile" ]; then
3067cdfce09SScott Long			# We're evaluating the contents of an external file.
3077cdfce09SScott Long			# Let's not open ourselves up for attack. _perms will
3087cdfce09SScott Long			# be empty if it's writeable only by the owner. _owner
3097cdfce09SScott Long			# will *NOT* be empty if the file is owned by root.
3107cdfce09SScott Long			#
311170d0882SDag-Erling Smørgrav			_dir="$(dirname "$msgfile")"
312170d0882SDag-Erling Smørgrav			_file="$(basename "$msgfile")"
313170d0882SDag-Erling Smørgrav			_perms=$(/usr/bin/find "$_dir" -name "$_file" -perm +07022 -prune)
314170d0882SDag-Erling Smørgrav			_owner=$(/usr/bin/find "$_dir" -name "$_file" -user 0 -prune)
315170d0882SDag-Erling Smørgrav			if [ -z "$_owner" ] || [ -n "$_perms" ]; then
3167cdfce09SScott Long				err "The message file ($msgfile) may be writeable only by root."
3177cdfce09SScott Long				return 1
3187cdfce09SScott Long			fi
3197cdfce09SScott Long			while read _line ; do
3207cdfce09SScott Long				eval echo "$_line"
321170d0882SDag-Erling Smørgrav			done <"$msgfile" | ${MAILCMD} -s"Welcome" ${username}
3227cdfce09SScott Long			info "Sent welcome message to ($username)."
323170d0882SDag-Erling Smørgrav		fi
3247cdfce09SScott Long	fi
3257cdfce09SScott Long}
3267cdfce09SScott Long
3277cdfce09SScott Long# get_user
3287cdfce09SScott Long#	Reads username of the account from standard input or from a global
3297cdfce09SScott Long#	variable containing an account line from a file. The username is
3307cdfce09SScott Long#	required. If this is an interactive session it will prompt in
3317cdfce09SScott Long#	a loop until a username is entered. If it is batch processing from
3327cdfce09SScott Long#	a file it will output an error message and return to the caller.
3337cdfce09SScott Long#
3347cdfce09SScott Longget_user() {
33546a619c6SDag-Erling Smørgrav	local _input=
3367cdfce09SScott Long
3377cdfce09SScott Long	# No need to take down user names if this is a configuration saving run.
3387cdfce09SScott Long	[ -n "$configflag" ] && return
3397cdfce09SScott Long
3407cdfce09SScott Long	while : ; do
3417cdfce09SScott Long		if [ -z "$fflag" ]; then
3427cdfce09SScott Long			echo -n "Username: "
3437cdfce09SScott Long			read _input
3447cdfce09SScott Long		else
345170d0882SDag-Erling Smørgrav			_input="$(echo "$fileline" | cut -f1 -d:)"
3467cdfce09SScott Long		fi
3477cdfce09SScott Long
348a80d527fSLukas Ertl		# There *must* be a username, and it must not exist. If
349a80d527fSLukas Ertl		# this is an interactive session give the user an
350a80d527fSLukas Ertl		# opportunity to retry.
3517cdfce09SScott Long		#
3527cdfce09SScott Long		if [ -z "$_input" ]; then
3537cdfce09SScott Long			err "You must enter a username!"
3547cdfce09SScott Long			[ -z "$fflag" ] && continue
3557cdfce09SScott Long		fi
356b3733389SDag-Erling Smørgrav		if ${PWCMD} usershow "$_input" > /dev/null 2>&1; then
357a80d527fSLukas Ertl			err "User exists!"
358a80d527fSLukas Ertl			[ -z "$fflag" ] && continue
359a80d527fSLukas Ertl		fi
3607cdfce09SScott Long		break
3617cdfce09SScott Long	done
3627cdfce09SScott Long	username="$_input"
3637cdfce09SScott Long}
3647cdfce09SScott Long
3657cdfce09SScott Long# get_gecos
3667cdfce09SScott Long#	Reads extra information about the user. Can be used both in interactive
3677cdfce09SScott Long#	and batch (from file) mode.
3687cdfce09SScott Long#
3697cdfce09SScott Longget_gecos() {
37046a619c6SDag-Erling Smørgrav	local _input=
3717cdfce09SScott Long
3727cdfce09SScott Long	# No need to take down additional user information for a configuration run.
3737cdfce09SScott Long	[ -n "$configflag" ] && return
3747cdfce09SScott Long
3757cdfce09SScott Long	if [ -z "$fflag" ]; then
3767cdfce09SScott Long		echo -n "Full name: "
3777cdfce09SScott Long		read _input
3787cdfce09SScott Long	else
379170d0882SDag-Erling Smørgrav		_input="$(echo "$fileline" | cut -f7 -d:)"
3807cdfce09SScott Long	fi
3817cdfce09SScott Long	ugecos="$_input"
3827cdfce09SScott Long}
3837cdfce09SScott Long
3847cdfce09SScott Long# get_shell
3857cdfce09SScott Long#	Get the account's shell. Works in interactive and batch mode. It
386e7291a6bSMike Makonnen#	accepts either the base name of the shell or the full path.
3877cdfce09SScott Long#	If an invalid shell is entered it will simply use the default shell.
3887cdfce09SScott Long#
3897cdfce09SScott Longget_shell() {
39046a619c6SDag-Erling Smørgrav	local _input= _fullpath=
3917cdfce09SScott Long	ushell="$defaultshell"
3927cdfce09SScott Long
3937cdfce09SScott Long	# Make sure the current value of the shell is a valid one
3940d640c0dSMike Makonnen	if [ -z "$Sflag" ]; then
3958923e98bSMike Makonnen		if ! shell_exists $ushell ; then
3968923e98bSMike Makonnen			info "Using default shell ${defaultshell}."
3977cdfce09SScott Long			ushell="$defaultshell"
3988923e98bSMike Makonnen		fi
3990d640c0dSMike Makonnen	fi
4007cdfce09SScott Long
4017cdfce09SScott Long	if [ -z "$fflag" ]; then
402170d0882SDag-Erling Smørgrav		echo -n "Shell ($shells) [${ushell##*/}]: "
4037cdfce09SScott Long		read _input
4047cdfce09SScott Long	else
405170d0882SDag-Erling Smørgrav		_input="$(echo "$fileline" | cut -f9 -d:)"
4067cdfce09SScott Long	fi
4077cdfce09SScott Long	if [ -n "$_input" ]; then
4080d640c0dSMike Makonnen		if [ -n "$Sflag" ]; then
4090d640c0dSMike Makonnen			ushell="$_input"
4100d640c0dSMike Makonnen		else
411170d0882SDag-Erling Smørgrav			_fullpath=$(fullpath_from_shell "$_input")
4127cdfce09SScott Long			if [ -n "$_fullpath" ]; then
4137cdfce09SScott Long				ushell="$_fullpath"
4147cdfce09SScott Long			else
4158923e98bSMike Makonnen				err "Invalid shell ($_input) for user $username."
4168923e98bSMike Makonnen				info "Using default shell ${defaultshell}."
4177cdfce09SScott Long				ushell="$defaultshell"
4187cdfce09SScott Long			fi
4197cdfce09SScott Long		fi
4200d640c0dSMike Makonnen	fi
4217cdfce09SScott Long}
4227cdfce09SScott Long
4237cdfce09SScott Long# get_homedir
4247cdfce09SScott Long#	Reads the account's home directory. Used both with interactive input
4257cdfce09SScott Long#	and batch input.
4267cdfce09SScott Long#
4277cdfce09SScott Longget_homedir() {
42846a619c6SDag-Erling Smørgrav	local _input=
4297cdfce09SScott Long	if [ -z "$fflag" ]; then
4307cdfce09SScott Long		echo -n "Home directory [${homeprefix}/${username}]: "
4317cdfce09SScott Long		read _input
4327cdfce09SScott Long	else
433170d0882SDag-Erling Smørgrav		_input="$(echo "$fileline" | cut -f8 -d:)"
4347cdfce09SScott Long	fi
4357cdfce09SScott Long
4367cdfce09SScott Long	if [ -n "$_input" ]; then
4377cdfce09SScott Long		uhome="$_input"
4387cdfce09SScott Long		# if this is a configuration run, then user input is the home
4397cdfce09SScott Long		# directory prefix. Otherwise it is understood to
4407cdfce09SScott Long		# be $prefix/$user
4417cdfce09SScott Long		#
442170d0882SDag-Erling Smørgrav		[ -z "$configflag" ] &&
443170d0882SDag-Erling Smørgrav		    homeprefix="$(dirname "$uhome")" ||
444170d0882SDag-Erling Smørgrav		    homeprefix="$uhome"
4457cdfce09SScott Long	else
4467cdfce09SScott Long		uhome="${homeprefix}/${username}"
4477cdfce09SScott Long	fi
4487cdfce09SScott Long}
4497cdfce09SScott Long
45043cb08ceSMike Makonnen# get_homeperm
45143cb08ceSMike Makonnen#	Reads the account's home directory permissions.
45243cb08ceSMike Makonnen#
45343cb08ceSMike Makonnenget_homeperm() {
45446a619c6SDag-Erling Smørgrav	local _input= _prompt=
45543cb08ceSMike Makonnen	uhomeperm=$defaultHomePerm
45643cb08ceSMike Makonnen
45743cb08ceSMike Makonnen	if [ -n "$uhomeperm" ]; then
45843cb08ceSMike Makonnen		_prompt="Home directory permissions [${uhomeperm}]: "
45943cb08ceSMike Makonnen	else
46043cb08ceSMike Makonnen		_prompt="Home directory permissions (Leave empty for default): "
46143cb08ceSMike Makonnen	fi
46243cb08ceSMike Makonnen	if [ -z "$fflag" ]; then
46343cb08ceSMike Makonnen		echo -n "$_prompt"
46443cb08ceSMike Makonnen		read _input
46543cb08ceSMike Makonnen	fi
46643cb08ceSMike Makonnen
46743cb08ceSMike Makonnen	if [ -n "$_input" ]; then
46843cb08ceSMike Makonnen		uhomeperm="$_input"
46943cb08ceSMike Makonnen	fi
47043cb08ceSMike Makonnen}
47143cb08ceSMike Makonnen
472215c0a51SJohn Grafton# get_zfs_home
473215c0a51SJohn Grafton#	Determine if homeprefix is located on a ZFS filesystem and if
474215c0a51SJohn Grafton#	so, enable ZFS home dataset creation.
475215c0a51SJohn Grafton#
476215c0a51SJohn Graftonget_zfs_home() {
477*0b39b2e2SMike Karels	local _prefix= _tmp=
478b3733389SDag-Erling Smørgrav
479215c0a51SJohn Grafton	# check if zfs kernel module is loaded before attempting to run zfs to
480215c0a51SJohn Grafton	# prevent loading the kernel module on systems that don't use ZFS
4819e861827SDag-Erling Smørgrav	if ! "$KLDSTATCMD" -q -m zfs; then
4829e861827SDag-Erling Smørgrav		Zcreate="no"
483215c0a51SJohn Grafton		return
484215c0a51SJohn Grafton	fi
485b3733389SDag-Erling Smørgrav	if ! _prefix=$(${ZFSCMD} list -Ho name "${homeprefix}" 2>/dev/null) ||
486b3733389SDag-Erling Smørgrav	    [ -z "${_prefix}" ]; then
487215c0a51SJohn Grafton		Zcreate="no"
488b3733389SDag-Erling Smørgrav		return
489215c0a51SJohn Grafton	fi
490*0b39b2e2SMike Karels	# Make sure that _prefix is not a subdirectory within a dataset.  If it
491*0b39b2e2SMike Karels	# is, the containing dataset will be the same for it and its parent.
492*0b39b2e2SMike Karels	_tmp=$(${ZFSCMD} list -Ho name "$(dirname "${homeprefix}")" 2>/dev/null)
493*0b39b2e2SMike Karels	if [ "${_tmp}" = "${_prefix}" ]; then
494*0b39b2e2SMike Karels		Zcreate="no"
495*0b39b2e2SMike Karels		return
496*0b39b2e2SMike Karels	fi
497b3733389SDag-Erling Smørgrav	zhome="${_prefix}/${username}"
498215c0a51SJohn Grafton}
499215c0a51SJohn Grafton
5007cdfce09SScott Long# get_uid
5017cdfce09SScott Long#	Reads a numeric userid in an interactive or batch session. Automatically
5027cdfce09SScott Long#	allocates one if it is not specified.
5037cdfce09SScott Long#
5047cdfce09SScott Longget_uid() {
50546a619c6SDag-Erling Smørgrav	local _input= _prompt=
5067cdfce09SScott Long	uuid=${uidstart}
5077cdfce09SScott Long
5087cdfce09SScott Long	if [ -n "$uuid" ]; then
509170d0882SDag-Erling Smørgrav		uuid=$(get_nextuid "$uuid")
5107cdfce09SScott Long		_prompt="Uid [$uuid]: "
5117cdfce09SScott Long	else
5127cdfce09SScott Long		_prompt="Uid (Leave empty for default): "
5137cdfce09SScott Long	fi
5147cdfce09SScott Long	if [ -z "$fflag" ]; then
5158b6caf26SMax Khon		echo -n "$_prompt"
5167cdfce09SScott Long		read _input
5177cdfce09SScott Long	else
518170d0882SDag-Erling Smørgrav		_input="$(echo "$fileline" | cut -f2 -d:)"
5197cdfce09SScott Long	fi
5207cdfce09SScott Long
5217cdfce09SScott Long	[ -n "$_input" ] && uuid=$_input
522170d0882SDag-Erling Smørgrav	uuid=$(get_nextuid "$uuid")
5237cdfce09SScott Long	uidstart=$uuid
5247cdfce09SScott Long}
5257cdfce09SScott Long
5267cdfce09SScott Long# get_class
5277cdfce09SScott Long#	Reads login class of account. Can be used in interactive or batch mode.
5287cdfce09SScott Long#
5297cdfce09SScott Longget_class() {
53046a619c6SDag-Erling Smørgrav	local _input= _class=
5317cdfce09SScott Long	uclass="$defaultclass"
5327cdfce09SScott Long	_class=${uclass:-"default"}
5337cdfce09SScott Long
5347cdfce09SScott Long	if [ -z "$fflag" ]; then
5357cdfce09SScott Long		echo -n "Login class [$_class]: "
5367cdfce09SScott Long		read _input
5377cdfce09SScott Long	else
538170d0882SDag-Erling Smørgrav		_input="$(echo "$fileline" | cut -f4 -d:)"
5397cdfce09SScott Long	fi
5407cdfce09SScott Long
5417cdfce09SScott Long	[ -n "$_input" ] && uclass="$_input"
5427cdfce09SScott Long}
5437cdfce09SScott Long
5447cdfce09SScott Long# get_logingroup
5457cdfce09SScott Long#	Reads user's login group. Can be used in both interactive and batch
5467cdfce09SScott Long#	modes. The specified value can be a group name or its numeric id.
547642a7af5SMike Makonnen#	This routine leaves the field blank if nothing is provided and
548642a7af5SMike Makonnen#	a default login group has not been set. The pw(8) command
549642a7af5SMike Makonnen#	will then provide a login group with the same name as the username.
5507cdfce09SScott Long#
5517cdfce09SScott Longget_logingroup() {
55246a619c6SDag-Erling Smørgrav	local _input=
553642a7af5SMike Makonnen	ulogingroup="$defaultLgroup"
5547cdfce09SScott Long
5557cdfce09SScott Long	if [ -z "$fflag" ]; then
556642a7af5SMike Makonnen		echo -n "Login group [${ulogingroup:-$username}]: "
5577cdfce09SScott Long		read _input
5587cdfce09SScott Long	else
559170d0882SDag-Erling Smørgrav		_input="$(echo "$fileline" | cut -f3 -d:)"
5607cdfce09SScott Long	fi
5617cdfce09SScott Long
5627cdfce09SScott Long	# Pw(8) will use the username as login group if it's left empty
563642a7af5SMike Makonnen	[ -n "$_input" ] && ulogingroup="$_input"
5647cdfce09SScott Long}
5657cdfce09SScott Long
5667cdfce09SScott Long# get_groups
5677cdfce09SScott Long#	Read additional groups for the user. It can be used in both interactive
5687cdfce09SScott Long#	and batch modes.
5697cdfce09SScott Long#
5707cdfce09SScott Longget_groups() {
57146a619c6SDag-Erling Smørgrav	local _input= _group=
5727cdfce09SScott Long	ugroups="$defaultgroups"
5737cdfce09SScott Long	_group=${ulogingroup:-"${username}"}
5747cdfce09SScott Long
5757cdfce09SScott Long	if [ -z "$configflag" ]; then
5767cdfce09SScott Long		[ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username"
5777cdfce09SScott Long		[ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: "
5787cdfce09SScott Long	else
5797cdfce09SScott Long		[ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: "
5807cdfce09SScott Long	fi
5817cdfce09SScott Long	read _input
5827cdfce09SScott Long
5837cdfce09SScott Long	[ -n "$_input" ] && ugroups="$_input"
5847cdfce09SScott Long}
5857cdfce09SScott Long
5867cdfce09SScott Long# get_expire_dates
5877cdfce09SScott Long#	Read expiry information for the account and also for the password. This
5887cdfce09SScott Long#	routine is used only from batch processing mode.
5897cdfce09SScott Long#
5907cdfce09SScott Longget_expire_dates() {
591170d0882SDag-Erling Smørgrav	upwexpire="$(echo "$fileline" | cut -f5 -d:)"
592170d0882SDag-Erling Smørgrav	uexpire="$(echo "$fileline" | cut -f6 -d:)"
5937cdfce09SScott Long}
5947cdfce09SScott Long
5957cdfce09SScott Long# get_password
5967cdfce09SScott Long#	Read the password in batch processing mode. The password field matters
5977cdfce09SScott Long#	only when the password type is "yes" or "random". If the field is empty and the
5987cdfce09SScott Long#	password type is "yes", then it assumes the account has an empty passsword
5997cdfce09SScott Long#	and changes the password type accordingly. If the password type is "random"
6007cdfce09SScott Long#	and the password field is NOT empty, then it assumes the account will NOT
6017cdfce09SScott Long#	have a random password and set passwdtype to "yes."
6027cdfce09SScott Long#
6037cdfce09SScott Longget_password() {
6047cdfce09SScott Long	# We may temporarily change a password type. Make sure it's changed
6057cdfce09SScott Long	# back to whatever it was before we process the next account.
6067cdfce09SScott Long	#
607170d0882SDag-Erling Smørgrav	if [ -n "$savedpwtype" ]; then
6087cdfce09SScott Long		passwdtype=$savedpwtype
6097cdfce09SScott Long		savedpwtype=
610170d0882SDag-Erling Smørgrav	fi
6117cdfce09SScott Long
6127cdfce09SScott Long	# There may be a ':' in the password
6137cdfce09SScott Long	upass=${fileline#*:*:*:*:*:*:*:*:*:}
6147cdfce09SScott Long
6157cdfce09SScott Long	if [ -z "$upass" ]; then
6167cdfce09SScott Long		case $passwdtype in
6177cdfce09SScott Long		yes)
6187cdfce09SScott Long			# if it's empty, assume an empty password
6197cdfce09SScott Long			passwdtype=none
6207cdfce09SScott Long			savedpwtype=yes
6217cdfce09SScott Long			;;
6227cdfce09SScott Long		esac
6237cdfce09SScott Long	else
6247cdfce09SScott Long		case $passwdtype in
6257cdfce09SScott Long		random)
6267cdfce09SScott Long			passwdtype=yes
6277cdfce09SScott Long			savedpwtype=random
6287cdfce09SScott Long			;;
6297cdfce09SScott Long		esac
6307cdfce09SScott Long	fi
6317cdfce09SScott Long}
6327cdfce09SScott Long
633215c0a51SJohn Grafton# get_zfs_encryption
634215c0a51SJohn Grafton#	Ask user if they want to enable encryption on their ZFS home dataset.
635215c0a51SJohn Grafton#
636215c0a51SJohn Graftonget_zfs_encryption() {
63746a619c6SDag-Erling Smørgrav	local _input= _prompt=
638215c0a51SJohn Grafton	_prompt="Enable ZFS encryption? (yes/no) [${Zencrypt}]: "
639215c0a51SJohn Grafton	while : ; do
640215c0a51SJohn Grafton		echo -n "$_prompt"
641215c0a51SJohn Grafton		read _input
642215c0a51SJohn Grafton
643215c0a51SJohn Grafton		[ -z "$_input" ] && _input=$Zencrypt
644215c0a51SJohn Grafton		case $_input in
645215c0a51SJohn Grafton		[Nn][Oo]|[Nn])
646215c0a51SJohn Grafton			Zencrypt="no"
647215c0a51SJohn Grafton			break
648215c0a51SJohn Grafton			;;
649215c0a51SJohn Grafton		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
650215c0a51SJohn Grafton			Zencrypt="yes"
651215c0a51SJohn Grafton			break
652215c0a51SJohn Grafton			;;
653215c0a51SJohn Grafton		*)
654215c0a51SJohn Grafton			# invalid answer; repeat loop
655215c0a51SJohn Grafton			continue
656215c0a51SJohn Grafton			;;
657215c0a51SJohn Grafton		esac
658215c0a51SJohn Grafton	done
659215c0a51SJohn Grafton
660215c0a51SJohn Grafton	if [ "${Zencrypt}" = "yes" ]; then
661215c0a51SJohn Grafton		zfsopt="-o encryption=on -o keylocation=prompt -o keyformat=passphrase"
662215c0a51SJohn Grafton	fi
663215c0a51SJohn Grafton}
664215c0a51SJohn Grafton
665215c0a51SJohn Grafton# create_zfs_chrooted_dataset
666215c0a51SJohn Grafton#   Create ZFS dataset owned by the user that was just added within a bsdinstall chroot
667215c0a51SJohn Grafton#
668215c0a51SJohn Graftoncreate_zfs_chrooted_dataset() {
669215c0a51SJohn Grafton	if ! ${ZFSCMD} create -u ${zfsopt} "${zhome}"; then
670215c0a51SJohn Grafton		err "There was an error creating ZFS dataset (${zhome})."
671215c0a51SJohn Grafton		return 1
672215c0a51SJohn Grafton	fi
673215c0a51SJohn Grafton	${ZFSCMD} set mountpoint=legacy "${zhome}"
674215c0a51SJohn Grafton	${MKDIRCMD} -p "${uhome}"
675215c0a51SJohn Grafton	${MOUNTCMD} -t zfs "${zhome}" "${uhome}"
676215c0a51SJohn Grafton}
677215c0a51SJohn Grafton
678215c0a51SJohn Grafton# umount_legacy_zfs
679215c0a51SJohn Grafton#   Unmount ZFS home directory created as a legacy mount and switch inheritance
680215c0a51SJohn Grafton#
681215c0a51SJohn Graftonumount_legacy_zfs() {
682215c0a51SJohn Grafton	${UMOUNTCMD} "${uhome}"
683215c0a51SJohn Grafton	${ZFSCMD} inherit mountpoint "${zhome}"
684215c0a51SJohn Grafton}
685215c0a51SJohn Grafton
686215c0a51SJohn Grafton# create_zfs_dataset
687215c0a51SJohn Grafton#   Create ZFS dataset owned by the user that was just added.
688215c0a51SJohn Grafton#
689215c0a51SJohn Graftoncreate_zfs_dataset() {
690215c0a51SJohn Grafton	if ! ${ZFSCMD} create ${zfsopt} "${zhome}"; then
691215c0a51SJohn Grafton		err "There was an error creating ZFS dataset (${zhome})."
692215c0a51SJohn Grafton		return 1
693215c0a51SJohn Grafton	else
694215c0a51SJohn Grafton		info "Successfully created ZFS dataset (${zhome})."
695215c0a51SJohn Grafton	fi
696215c0a51SJohn Grafton}
697215c0a51SJohn Grafton
698215c0a51SJohn Grafton# set_zfs_perms
699215c0a51SJohn Grafton#   Give new user ownership of newly created zfs dataset.
700215c0a51SJohn Grafton#
701215c0a51SJohn Graftonset_zfs_perms() {
702215c0a51SJohn Grafton	if ! ${ZFSCMD} allow "${username}" create,destroy,mount,snapshot "${zhome}"; then
703215c0a51SJohn Grafton		err "There was an error setting permissions on ZFS dataset (${zhome})."
704215c0a51SJohn Grafton		return 1
705215c0a51SJohn Grafton	fi
706215c0a51SJohn Grafton}
707215c0a51SJohn Grafton
7087cdfce09SScott Long# input_from_file
7097cdfce09SScott Long#	Reads a line of account information from standard input and
7107cdfce09SScott Long#	adds it to the user database.
7117cdfce09SScott Long#
7127cdfce09SScott Longinput_from_file() {
71346a619c6SDag-Erling Smørgrav	local _field=
7147cdfce09SScott Long
715c95c14d4SMike Makonnen	while read -r fileline ; do
7167cdfce09SScott Long		case "$fileline" in
7177cdfce09SScott Long		\#*|'')
7187cdfce09SScott Long			;;
719f30cdb6eSMike Makonnen		*)
7207cdfce09SScott Long			get_user || continue
7217cdfce09SScott Long			get_gecos
7227cdfce09SScott Long			get_uid
7237cdfce09SScott Long			get_logingroup
7247cdfce09SScott Long			get_class
7257cdfce09SScott Long			get_shell
7267cdfce09SScott Long			get_homedir
727215c0a51SJohn Grafton			get_zfs_home
72843cb08ceSMike Makonnen			get_homeperm
7297cdfce09SScott Long			get_password
7307cdfce09SScott Long			get_expire_dates
73188b63febSMike Makonnen			ugroups="$defaultgroups"
7327cdfce09SScott Long
7337cdfce09SScott Long			add_user
734f30cdb6eSMike Makonnen			;;
735f30cdb6eSMike Makonnen		esac
7367cdfce09SScott Long	done
7377cdfce09SScott Long}
7387cdfce09SScott Long
7397cdfce09SScott Long# input_interactive
7407cdfce09SScott Long#	Prompts for user information interactively, and commits to
7417cdfce09SScott Long#	the user database.
7427cdfce09SScott Long#
7437cdfce09SScott Longinput_interactive() {
74446a619c6SDag-Erling Smørgrav	local _disable= _pass= _passconfirm= _input=
745170d0882SDag-Erling Smørgrav	local _random="no"
746170d0882SDag-Erling Smørgrav	local _emptypass="no"
747170d0882SDag-Erling Smørgrav	local _usepass="yes"
748170d0882SDag-Erling Smørgrav	local _logingroup_ok="no"
749170d0882SDag-Erling Smørgrav	local _groups_ok="no"
750170d0882SDag-Erling Smørgrav	local _all_ok="yes"
7517cdfce09SScott Long	case $passwdtype in
7527cdfce09SScott Long	none)
7537cdfce09SScott Long		_emptypass="yes"
7547cdfce09SScott Long		_usepass="yes"
7557cdfce09SScott Long		;;
7567cdfce09SScott Long	no)
7577cdfce09SScott Long		_usepass="no"
7587cdfce09SScott Long		;;
7597cdfce09SScott Long	random)
7607cdfce09SScott Long		_random="yes"
7617cdfce09SScott Long		;;
7627cdfce09SScott Long	esac
7637cdfce09SScott Long
7647cdfce09SScott Long	get_user
7657cdfce09SScott Long	get_gecos
7667cdfce09SScott Long	get_uid
7673386ded6SAdrian Chadd
7683386ded6SAdrian Chadd	# The case where group = user is handled elsewhere, so
7693386ded6SAdrian Chadd	# validate any other groups the user is invited to.
7703386ded6SAdrian Chadd	until [ "$_logingroup_ok" = yes ]; do
7717cdfce09SScott Long		get_logingroup
7723386ded6SAdrian Chadd		_logingroup_ok=yes
773170d0882SDag-Erling Smørgrav		if [ -n "$ulogingroup" ] && [ "$username" != "$ulogingroup" ]; then
7743386ded6SAdrian Chadd			if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then
7753386ded6SAdrian Chadd				echo "Group $ulogingroup does not exist!"
7763386ded6SAdrian Chadd				_logingroup_ok=no
7773386ded6SAdrian Chadd			fi
7783386ded6SAdrian Chadd		fi
7793386ded6SAdrian Chadd	done
7803386ded6SAdrian Chadd	until [ "$_groups_ok" = yes ]; do
7817cdfce09SScott Long		get_groups
7823386ded6SAdrian Chadd		_groups_ok=yes
7833386ded6SAdrian Chadd		for i in $ugroups; do
7843386ded6SAdrian Chadd			if [ "$username" != "$i" ]; then
7853386ded6SAdrian Chadd				if ! ${PWCMD} show group $i > /dev/null 2>&1; then
7863386ded6SAdrian Chadd					echo "Group $i does not exist!"
7873386ded6SAdrian Chadd					_groups_ok=no
7883386ded6SAdrian Chadd				fi
7893386ded6SAdrian Chadd			fi
7903386ded6SAdrian Chadd		done
7913386ded6SAdrian Chadd	done
7923386ded6SAdrian Chadd
7937cdfce09SScott Long	get_class
7947cdfce09SScott Long	get_shell
7957cdfce09SScott Long	get_homedir
79643cb08ceSMike Makonnen	get_homeperm
797215c0a51SJohn Grafton	get_zfs_home
798215c0a51SJohn Grafton	[ "$Zcreate" = "yes" ] && get_zfs_encryption
7997cdfce09SScott Long
8007cdfce09SScott Long	while : ; do
8017cdfce09SScott Long		echo -n "Use password-based authentication? [$_usepass]: "
8027cdfce09SScott Long		read _input
8037cdfce09SScott Long		[ -z "$_input" ] && _input=$_usepass
8047cdfce09SScott Long		case $_input in
8057cdfce09SScott Long		[Nn][Oo]|[Nn])
8067cdfce09SScott Long			passwdtype="no"
8077cdfce09SScott Long			;;
8087cdfce09SScott Long		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
8097cdfce09SScott Long			while : ; do
8107cdfce09SScott Long				echo -n "Use an empty password? (yes/no) [$_emptypass]: "
8117cdfce09SScott Long				read _input
8127cdfce09SScott Long				[ -n "$_input" ] && _emptypass=$_input
8137cdfce09SScott Long				case $_emptypass in
8147cdfce09SScott Long				[Nn][Oo]|[Nn])
8157cdfce09SScott Long					echo -n "Use a random password? (yes/no) [$_random]: "
8167cdfce09SScott Long					read _input
8177cdfce09SScott Long					[ -n "$_input" ] && _random="$_input"
8187cdfce09SScott Long					case $_random in
8197cdfce09SScott Long					[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
8207cdfce09SScott Long						passwdtype="random"
8217cdfce09SScott Long						break
8227cdfce09SScott Long						;;
8237cdfce09SScott Long					esac
8247cdfce09SScott Long					passwdtype="yes"
825187a97aaSMike Makonnen					[ -n "$configflag" ] && break
8267cdfce09SScott Long					trap 'stty echo; exit' 0 1 2 3 15
8277cdfce09SScott Long					stty -echo
8287cdfce09SScott Long					echo -n "Enter password: "
829f5339b09SKyle Evans					IFS= read -r upass
8307cdfce09SScott Long					echo''
8317cdfce09SScott Long					echo -n "Enter password again: "
832f5339b09SKyle Evans					IFS= read -r _passconfirm
8337cdfce09SScott Long					echo ''
8347cdfce09SScott Long					stty echo
8357cdfce09SScott Long					# if user entered a blank password
8367cdfce09SScott Long					# explicitly ask again.
837170d0882SDag-Erling Smørgrav					[ -z "$upass$_passconfirm" ] && continue
8387cdfce09SScott Long					;;
8397cdfce09SScott Long				[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
8407cdfce09SScott Long					passwdtype="none"
8417cdfce09SScott Long					break;
8427cdfce09SScott Long					;;
8437cdfce09SScott Long				*)
8447cdfce09SScott Long					# invalid answer; repeat the loop
8457cdfce09SScott Long					continue
8467cdfce09SScott Long					;;
8477cdfce09SScott Long				esac
8487cdfce09SScott Long				if [ "$upass" != "$_passconfirm" ]; then
8497cdfce09SScott Long					echo "Passwords did not match!"
8507cdfce09SScott Long					continue
8517cdfce09SScott Long				fi
8527cdfce09SScott Long				break
8537cdfce09SScott Long			done
8547cdfce09SScott Long			;;
8557cdfce09SScott Long		*)
8567cdfce09SScott Long			# invalid answer; repeat loop
8577cdfce09SScott Long			continue
8587cdfce09SScott Long			;;
8597cdfce09SScott Long		esac
8607cdfce09SScott Long		break;
8617cdfce09SScott Long	done
8627cdfce09SScott Long	_disable=${disableflag:-"no"}
8637cdfce09SScott Long	while : ; do
8647cdfce09SScott Long		echo -n "Lock out the account after creation? [$_disable]: "
8657cdfce09SScott Long		read _input
8667cdfce09SScott Long		[ -z "$_input" ] && _input=$_disable
8677cdfce09SScott Long		case $_input in
8687cdfce09SScott Long		[Nn][Oo]|[Nn])
8697cdfce09SScott Long			disableflag=
8707cdfce09SScott Long			;;
8717cdfce09SScott Long		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
8727cdfce09SScott Long			disableflag=yes
8737cdfce09SScott Long			;;
8747cdfce09SScott Long		*)
8757cdfce09SScott Long			# invalid answer; repeat loop
8767cdfce09SScott Long			continue
8777cdfce09SScott Long			;;
8787cdfce09SScott Long		esac
8797cdfce09SScott Long		break
8807cdfce09SScott Long	done
8817cdfce09SScott Long
8827cdfce09SScott Long	# Display the information we have so far and prompt to
8837cdfce09SScott Long	# commit it.
8847cdfce09SScott Long	#
8857cdfce09SScott Long	_disable=${disableflag:-"no"}
886215c0a51SJohn Grafton	[ -z "$configflag" ] && printf "%-11s : %s\n" Username $username
8877cdfce09SScott Long	case $passwdtype in
8887cdfce09SScott Long	yes)
8897cdfce09SScott Long		_pass='*****'
8907cdfce09SScott Long		;;
8917cdfce09SScott Long	no)
8927cdfce09SScott Long		_pass='<disabled>'
8937cdfce09SScott Long		;;
8947cdfce09SScott Long	none)
8957cdfce09SScott Long		_pass='<blank>'
8967cdfce09SScott Long		;;
8977cdfce09SScott Long	random)
8987cdfce09SScott Long		_pass='<random>'
8997cdfce09SScott Long		;;
9007cdfce09SScott Long	esac
901215c0a51SJohn Grafton	[ -z "$configflag" ] && printf "%-11s : %s\n" "Password" "$_pass"
902215c0a51SJohn Grafton	[ -n "$configflag" ] && printf "%-11s : %s\n" "Pass Type" "$passwdtype"
903215c0a51SJohn Grafton	[ -z "$configflag" ] && printf "%-11s : %s\n" "Full Name" "$ugecos"
904215c0a51SJohn Grafton	[ -z "$configflag" ] && printf "%-11s : %s\n" "Uid" "$uuid"
905170d0882SDag-Erling Smørgrav	[ "$Zcreate" = "yes" ] && [ -z "$configflag" ] &&
906170d0882SDag-Erling Smørgrav	    printf "%-11s : %s\n" "ZFS dataset" "${zhome}"
907170d0882SDag-Erling Smørgrav	[ "$Zencrypt" = "yes" ] && [ -z "$configflag" ] &&
908170d0882SDag-Erling Smørgrav	    printf "%-11s : %s\n" "Encrypted" "${Zencrypt}"
909215c0a51SJohn Grafton	printf "%-11s : %s\n" "Class" "$uclass"
910215c0a51SJohn Grafton	printf "%-11s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups"
911215c0a51SJohn Grafton	printf "%-11s : %s\n" "Home" "$uhome"
912215c0a51SJohn Grafton	printf "%-11s : %s\n" "Home Mode" "$uhomeperm"
913215c0a51SJohn Grafton	printf "%-11s : %s\n" "Shell" "$ushell"
914215c0a51SJohn Grafton	printf "%-11s : %s\n" "Locked" "$_disable"
9157cdfce09SScott Long	while : ; do
9169efad6f9SSven Ruediger		echo -n "OK? (yes/no) [$_all_ok]: "
9177cdfce09SScott Long		read _input
9189efad6f9SSven Ruediger		if [ -z "$_input" ]; then
9199efad6f9SSven Ruediger			_input=$_all_ok
9209efad6f9SSven Ruediger		fi
9217cdfce09SScott Long		case $_input in
9227cdfce09SScott Long		[Nn][Oo]|[Nn])
9237cdfce09SScott Long			return 1
9247cdfce09SScott Long			;;
9257cdfce09SScott Long		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
9267cdfce09SScott Long			add_user
9277cdfce09SScott Long			;;
9287cdfce09SScott Long		*)
9297cdfce09SScott Long			continue
9307cdfce09SScott Long			;;
9317cdfce09SScott Long		esac
9327cdfce09SScott Long		break
9337cdfce09SScott Long	done
9347cdfce09SScott Long	return 0
9357cdfce09SScott Long}
9367cdfce09SScott Long
937989090c0SRobert Drehmel#### END SUBROUTINE DEFINITION ####
9387cdfce09SScott Long
939170d0882SDag-Erling SmørgravTHISCMD=${0##*/}
9407cdfce09SScott LongDEFAULTSHELL=/bin/sh
9417cdfce09SScott LongADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
9427cdfce09SScott LongPWCMD="${PWCMD:-/usr/sbin/pw}"
9437cdfce09SScott LongMAILCMD="${MAILCMD:-mail}"
9447cdfce09SScott LongETCSHELLS="${ETCSHELLS:-/etc/shells}"
9450d640c0dSMike MakonnenNOHOME="/nonexistent"
9468923e98bSMike MakonnenNOLOGIN="nologin"
94768050033SColin PercivalNOLOGIN_PATH="/usr/sbin/nologin"
948e63bd70aSMax KhonGREPCMD="/usr/bin/grep"
949e63bd70aSMax KhonDATECMD="/bin/date"
950215c0a51SJohn GraftonMKDIRCMD="/bin/mkdir"
951215c0a51SJohn GraftonMOUNTCMD="/sbin/mount"
952215c0a51SJohn GraftonUMOUNTCMD="/sbin/umount"
953215c0a51SJohn GraftonZFSCMD="/sbin/zfs"
954215c0a51SJohn GraftonKLDSTATCMD="/sbin/kldstat"
9557cdfce09SScott Long
9567cdfce09SScott Long# Set default values
9577cdfce09SScott Long#
9587cdfce09SScott Longusername=
9597cdfce09SScott Longuuid=
9607cdfce09SScott Longuidstart=
9617cdfce09SScott Longugecos=
9627cdfce09SScott Longulogingroup=
9637cdfce09SScott Longuclass=
9647cdfce09SScott Longuhome=
96543cb08ceSMike Makonnenuhomeperm=
9667cdfce09SScott Longupass=
9677cdfce09SScott Longushell=
9687cdfce09SScott Longudotdir=/usr/share/skel
9697cdfce09SScott Longugroups=
9707cdfce09SScott Longuexpire=
9717cdfce09SScott Longupwexpire=
972170d0882SDag-Erling Smørgravshells="$(valid_shells)"
9737cdfce09SScott Longpasswdtype="yes"
9747cdfce09SScott Longmsgfile=/etc/adduser.msg
9757cdfce09SScott Longmsgflag=
9767cdfce09SScott Longquietflag=
9777cdfce09SScott Longconfigflag=
9787cdfce09SScott Longfflag=
9797cdfce09SScott Longinfile=
9807cdfce09SScott Longdisableflag=
9810d640c0dSMike MakonnenDflag=
9820d640c0dSMike MakonnenSflag=
983215c0a51SJohn GraftonZcreate="yes"
9847cdfce09SScott Longreadconfig="yes"
9857cdfce09SScott Longhomeprefix="/home"
9867cdfce09SScott Longrandompass=
9877cdfce09SScott Longfileline=
9887cdfce09SScott Longsavedpwtype=
9897cdfce09SScott Longdefaultclass=
990642a7af5SMike MakonnendefaultLgroup=
991fdbc43bdSMike Makonnendefaultgroups=
9927cdfce09SScott Longdefaultshell="${DEFAULTSHELL}"
99343cb08ceSMike MakonnendefaultHomePerm=
994215c0a51SJohn Graftonzfsopt=
995215c0a51SJohn GraftonZencrypt="no"
9967cdfce09SScott Long
9977cdfce09SScott Long# Make sure the user running this program is root. This isn't a security
99805c9bdf5SJoel Dahl# measure as much as it is a useful method of reminding the user to
9997cdfce09SScott Long# 'su -' before he/she wastes time entering data that won't be saved.
10007cdfce09SScott Long#
1001170d0882SDag-Erling Smørgravprocowner=${procowner:-$(/usr/bin/id -u)}
10027cdfce09SScott Longif [ "$procowner" != "0" ]; then
10037cdfce09SScott Long	err 'you must be the super-user (uid 0) to use this utility.'
10047cdfce09SScott Long	exit 1
10057cdfce09SScott Longfi
10067cdfce09SScott Long
10073df5ecacSUlrich Spörlein# Override from our conf file
10087cdfce09SScott Long# Quickly go through the commandline line to see if we should read
10097cdfce09SScott Long# from our configuration file. The actual parsing of the commandline
10107cdfce09SScott Long# arguments happens after we read in our configuration file (commandline
10117cdfce09SScott Long# should override configuration file).
10127cdfce09SScott Long#
10137cdfce09SScott Longfor _i in $* ; do
10147cdfce09SScott Long	if [ "$_i" = "-N" ]; then
10157cdfce09SScott Long		readconfig=
10167cdfce09SScott Long		break;
10177cdfce09SScott Long	fi
10187cdfce09SScott Longdone
1019170d0882SDag-Erling Smørgravif [ -n "$readconfig" ] && [ -r "${ADDUSERCONF}" ]; then
1020170d0882SDag-Erling Smørgrav	. "${ADDUSERCONF}"
10217cdfce09SScott Longfi
10227cdfce09SScott Long
10233df5ecacSUlrich Spörlein# Process command-line options
10247cdfce09SScott Long#
10257cdfce09SScott Longfor _switch ; do
10267cdfce09SScott Long	case $_switch in
10277cdfce09SScott Long	-L)
10287cdfce09SScott Long		defaultclass="$2"
10297cdfce09SScott Long		shift; shift
10307cdfce09SScott Long		;;
10317cdfce09SScott Long	-C)
10327cdfce09SScott Long		configflag=yes
10337cdfce09SScott Long		shift
10347cdfce09SScott Long		;;
10350d640c0dSMike Makonnen	-D)
10360d640c0dSMike Makonnen		Dflag=yes
10370d640c0dSMike Makonnen		shift
10380d640c0dSMike Makonnen		;;
10397cdfce09SScott Long	-E)
10407cdfce09SScott Long		disableflag=yes
10417cdfce09SScott Long		shift
10427cdfce09SScott Long		;;
10437cdfce09SScott Long	-k)
10447cdfce09SScott Long		udotdir="$2"
10457cdfce09SScott Long		shift; shift
10467cdfce09SScott Long		;;
10477cdfce09SScott Long	-f)
10487cdfce09SScott Long		[ "$2" != "-" ] && infile="$2"
10497cdfce09SScott Long		fflag=yes
10507cdfce09SScott Long		shift; shift
10517cdfce09SScott Long		;;
1052642a7af5SMike Makonnen	-g)
1053642a7af5SMike Makonnen		defaultLgroup="$2"
1054642a7af5SMike Makonnen		shift; shift
1055642a7af5SMike Makonnen		;;
10567cdfce09SScott Long	-G)
10577cdfce09SScott Long		defaultgroups="$2"
10587cdfce09SScott Long		shift; shift
10597cdfce09SScott Long		;;
10607cdfce09SScott Long	-h)
10617cdfce09SScott Long		show_usage
10627cdfce09SScott Long		exit 0
10637cdfce09SScott Long		;;
10647cdfce09SScott Long	-d)
10657cdfce09SScott Long		homeprefix="$2"
10667cdfce09SScott Long		shift; shift
10677cdfce09SScott Long		;;
10687cdfce09SScott Long	-m)
10697cdfce09SScott Long		case "$2" in
10707cdfce09SScott Long		[Nn][Oo])
10717cdfce09SScott Long			msgflag=
10727cdfce09SScott Long			;;
10737cdfce09SScott Long		*)
10747cdfce09SScott Long			msgflag=yes
10757cdfce09SScott Long			msgfile="$2"
10767cdfce09SScott Long			;;
10777cdfce09SScott Long		esac
10787cdfce09SScott Long		shift; shift
10797cdfce09SScott Long		;;
108043cb08ceSMike Makonnen	-M)
108143cb08ceSMike Makonnen		defaultHomePerm=$2
108243cb08ceSMike Makonnen		shift; shift
108343cb08ceSMike Makonnen		;;
10847cdfce09SScott Long	-N)
10857cdfce09SScott Long		readconfig=
10867cdfce09SScott Long		shift
10877cdfce09SScott Long		;;
10887cdfce09SScott Long	-w)
10897cdfce09SScott Long		case "$2" in
10907cdfce09SScott Long		no|none|random|yes)
10917cdfce09SScott Long			passwdtype=$2
10927cdfce09SScott Long			;;
10937cdfce09SScott Long		*)
10947cdfce09SScott Long			show_usage
10957cdfce09SScott Long			exit 1
10967cdfce09SScott Long			;;
10977cdfce09SScott Long		esac
10987cdfce09SScott Long		shift; shift
10997cdfce09SScott Long		;;
11007cdfce09SScott Long	-q)
11017cdfce09SScott Long		quietflag=yes
11027cdfce09SScott Long		shift
11037cdfce09SScott Long		;;
11047cdfce09SScott Long	-s)
1105170d0882SDag-Erling Smørgrav		defaultshell="$(fullpath_from_shell $2)"
11067cdfce09SScott Long		shift; shift
11077cdfce09SScott Long		;;
11080d640c0dSMike Makonnen	-S)
11090d640c0dSMike Makonnen		Sflag=yes
11100d640c0dSMike Makonnen		shift
11110d640c0dSMike Makonnen		;;
11127cdfce09SScott Long	-u)
11137cdfce09SScott Long		uidstart=$2
11147cdfce09SScott Long		shift; shift
11157cdfce09SScott Long		;;
1116215c0a51SJohn Grafton	-Z)
1117215c0a51SJohn Grafton		Zcreate="no"
1118215c0a51SJohn Grafton		shift
1119215c0a51SJohn Grafton		;;
11207cdfce09SScott Long	esac
11217cdfce09SScott Longdone
11227cdfce09SScott Long
11237cdfce09SScott Long# If the -f switch was used, get input from a file. Otherwise,
11247cdfce09SScott Long# this is an interactive session.
11257cdfce09SScott Long#
11267cdfce09SScott Longif [ -n "$fflag" ]; then
11277cdfce09SScott Long	if [ -z "$infile" ]; then
11287cdfce09SScott Long		input_from_file
11297cdfce09SScott Long	elif [ -n "$infile" ]; then
11307cdfce09SScott Long		if [ -r "$infile" ]; then
11317cdfce09SScott Long			input_from_file < $infile
11327cdfce09SScott Long		else
11337cdfce09SScott Long			err "File ($infile) is unreadable or does not exist."
11347cdfce09SScott Long		fi
11357cdfce09SScott Long	fi
11367cdfce09SScott Longelse
11377cdfce09SScott Long	input_interactive
113821084fe3SMike Makonnen	while : ; do
113946c4e86eSDag-Erling Smørgrav		_another_user="no"
1140187a97aaSMike Makonnen		if [ -z "$configflag" ]; then
11419efad6f9SSven Ruediger			echo -n "Add another user? (yes/no) [$_another_user]: "
1142187a97aaSMike Makonnen		else
11439efad6f9SSven Ruediger			echo -n "Re-edit the default configuration? (yes/no) [$_another_user]: "
1144187a97aaSMike Makonnen		fi
114521084fe3SMike Makonnen		read _input
11469efad6f9SSven Ruediger		if [ -z "$_input" ]; then
11479efad6f9SSven Ruediger			_input=$_another_user
11489efad6f9SSven Ruediger		fi
114921084fe3SMike Makonnen		case $_input in
115021084fe3SMike Makonnen		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
1151170d0882SDag-Erling Smørgrav			uidstart=$(get_nextuid $uidstart)
115221084fe3SMike Makonnen			input_interactive
115321084fe3SMike Makonnen			continue
115421084fe3SMike Makonnen			;;
115521084fe3SMike Makonnen		[Nn][Oo]|[Nn])
115621084fe3SMike Makonnen			echo "Goodbye!"
115721084fe3SMike Makonnen			;;
115821084fe3SMike Makonnen		*)
115921084fe3SMike Makonnen			continue
116021084fe3SMike Makonnen			;;
116121084fe3SMike Makonnen		esac
116221084fe3SMike Makonnen		break
116321084fe3SMike Makonnen	done
11647cdfce09SScott Longfi
1165