1107543Sscottl#!/bin/sh
2107543Sscottl#
3330449Seadler# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4330449Seadler#
5127076Smtm# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved.
6107543Sscottl#
7107543Sscottl# Redistribution and use in source and binary forms, with or without
8107543Sscottl# modification, are permitted provided that the following conditions
9107543Sscottl# are met:
10107543Sscottl# 1. Redistributions of source code must retain the above copyright
11107543Sscottl#    notice, this list of conditions and the following disclaimer.
12107543Sscottl# 2. Redistributions in binary form must reproduce the above copyright
13107543Sscottl#    notice, this list of conditions and the following disclaimer in the
14107543Sscottl#    documentation and/or other materials provided with the distribution.
15107543Sscottl#
16107543Sscottl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17107543Sscottl# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18107543Sscottl# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19107543Sscottl# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20107543Sscottl# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21107543Sscottl# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22107543Sscottl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23107543Sscottl# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24107543Sscottl# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25107543Sscottl# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26107543Sscottl#
27116624Smtm#       Email: Mike Makonnen <mtm@FreeBSD.Org>
28107543Sscottl#
29107543Sscottl# $FreeBSD: stable/11/usr.sbin/adduser/adduser.sh 360390 2020-04-27 19:29:48Z kevans $
30107543Sscottl#
31107543Sscottl
32107543Sscottl# err msg
33202312Sjoel#	Display $msg on stderr, unless we're being quiet.
34202312Sjoel#
35107543Sscottlerr() {
36107543Sscottl	if [ -z "$quietflag" ]; then
37202312Sjoel		echo 1>&2 ${THISCMD}: ERROR: $*
38107543Sscottl	fi
39107543Sscottl}
40107543Sscottl
41107543Sscottl# info msg
42202312Sjoel#	Display $msg on stdout, unless we're being quiet.
43202312Sjoel#
44107543Sscottlinfo() {
45107543Sscottl	if [ -z "$quietflag" ]; then
46202312Sjoel		echo ${THISCMD}: INFO: $*
47107543Sscottl	fi
48107543Sscottl}
49107543Sscottl
50107543Sscottl# get_nextuid
51107543Sscottl#	Output the value of $_uid if it is available for use. If it
52107543Sscottl#	is not, output the value of the next higher uid that is available.
53107543Sscottl#	If a uid is not specified, output the first available uid, as indicated
54107543Sscottl#	by pw(8).
55202312Sjoel#
56107543Sscottlget_nextuid () {
57107543Sscottl	_uid=$1
58107543Sscottl	_nextuid=
59107543Sscottl
60107543Sscottl	if [ -z "$_uid" ]; then
61107543Sscottl		_nextuid="`${PWCMD} usernext | cut -f1 -d:`"
62107543Sscottl	else
63107543Sscottl		while : ; do
64107543Sscottl			${PWCMD} usershow $_uid > /dev/null 2>&1
65107543Sscottl			if [ ! "$?" -eq 0 ]; then
66107543Sscottl				_nextuid=$_uid
67107543Sscottl				break
68107543Sscottl			fi
69107543Sscottl			_uid=$(($_uid + 1))
70107543Sscottl		done
71107543Sscottl	fi
72107543Sscottl	echo $_nextuid
73107543Sscottl}
74107543Sscottl
75107543Sscottl# show_usage
76107543Sscottl#	Display usage information for this utility.
77107543Sscottl#
78107543Sscottlshow_usage() {
79107543Sscottl	echo "usage: ${THISCMD} [options]"
80107543Sscottl	echo "  options may include:"
81107543Sscottl	echo "  -C		save to the configuration file only"
82127076Smtm	echo "  -D		do not attempt to create the home directory"
83107543Sscottl	echo "  -E		disable this account after creation"
84107543Sscottl	echo "  -G		additional groups to add accounts to"
85107543Sscottl	echo "  -L		login class of the user"
86175719Smtm	echo "  -M		file permission for home directory"
87107543Sscottl	echo "  -N		do not read configuration file"
88127076Smtm	echo "  -S		a nonexistent shell is not an error"
89107543Sscottl	echo "  -d		home directory"
90107543Sscottl	echo "  -f		file from which input will be received"
91112519Smtm	echo "  -g		default login group"
92107543Sscottl	echo "  -h		display this usage message"
93107543Sscottl	echo "  -k		path to skeleton home directory"
94107543Sscottl	echo "  -m		user welcome message file"
95107543Sscottl	echo "  -q		absolute minimal user feedback"
96107543Sscottl	echo "  -s		shell"
97107543Sscottl	echo "  -u		uid to start at"
98107543Sscottl	echo "  -w		password type: no, none, yes or random"
99107543Sscottl}
100107543Sscottl
101107543Sscottl# valid_shells
102107543Sscottl#	Outputs a list of valid shells from /etc/shells. Only the
103107543Sscottl#	basename of the shell is output.
104107543Sscottl#
105107543Sscottlvalid_shells() {
106107543Sscottl	_prefix=
107107543Sscottl	cat ${ETCSHELLS} |
108107543Sscottl	while read _path _junk ; do
109107543Sscottl		case $_path in
110107543Sscottl		\#*|'')
111107543Sscottl			;;
112107543Sscottl		*)
113107543Sscottl			echo -n "${_prefix}`basename $_path`"
114107543Sscottl			_prefix=' '
115107543Sscottl			;;
116107543Sscottl		esac
117107543Sscottl	done
118116627Smtm
119127635Scperciva	# /usr/sbin/nologin is a special case
120116627Smtm	[ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}"
121107543Sscottl}
122107543Sscottl
123107543Sscottl# fullpath_from_shell shell
124130160Smtm#	Given $shell, which is either the full path to a shell or
125130160Smtm#	the basename component of a valid shell, get the
126107543Sscottl#	full path to the shell from the /etc/shells file.
127107543Sscottl#
128107543Sscottlfullpath_from_shell() {
129107543Sscottl	_shell=$1
130107543Sscottl	[ -z "$_shell" ] && return 1
131107543Sscottl
132135616Sroam	# /usr/sbin/nologin is a special case; it needs to be handled
133135616Sroam	# before the cat | while loop, since a 'return' from within
134135616Sroam	# a subshell will not terminate the function's execution, and
135135616Sroam	# the path to the nologin shell might be printed out twice.
136135616Sroam	#
137135616Sroam	if [ "$_shell" = "${NOLOGIN}" -o \
138135616Sroam	    "$_shell" = "${NOLOGIN_PATH}" ]; then
139135616Sroam		echo ${NOLOGIN_PATH}
140135616Sroam		return 0;
141135616Sroam	fi
142135616Sroam
143107543Sscottl	cat ${ETCSHELLS} |
144107543Sscottl	while read _path _junk ; do
145107543Sscottl		case "$_path" in
146107543Sscottl		\#*|'')
147107543Sscottl			;;
148107543Sscottl		*)
149130160Smtm			if [ "$_path" = "$_shell" -o \
150130160Smtm			    "`basename $_path`" = "$_shell" ]; then
151107543Sscottl				echo $_path
152107543Sscottl				return 0
153107543Sscottl			fi
154107543Sscottl			;;
155107543Sscottl		esac
156107543Sscottl	done
157116627Smtm
158107543Sscottl	return 1
159107543Sscottl}
160107543Sscottl
161116627Smtm# shell_exists shell
162116627Smtm#	If the given shell is listed in ${ETCSHELLS} or it is
163116627Smtm#	the nologin shell this function will return 0.
164116627Smtm#	Otherwise, it will return 1. If shell is valid but
165116627Smtm#	the path is invalid or it is not executable it
166116627Smtm#	will emit an informational message saying so.
167116627Smtm#
168202312Sjoelshell_exists() {
169116627Smtm	_sh="$1"
170116627Smtm	_shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1"
171116627Smtm
172116627Smtm	if ! eval $_shellchk; then
173116627Smtm		# The nologin shell is not listed in /etc/shells.
174116627Smtm		if [ "$_sh" != "${NOLOGIN_PATH}" ]; then
175116627Smtm			err "Invalid shell ($_sh) for user $username."
176116627Smtm			return 1
177116627Smtm		fi
178116627Smtm	fi
179116627Smtm	! [ -x "$_sh" ] &&
180116628Smtm	    info "The shell ($_sh) does not exist or is not executable."
181116627Smtm
182116627Smtm	return 0
183116627Smtm}
184116627Smtm
185107543Sscottl# save_config
186107543Sscottl#	Save some variables to a configuration file.
187107543Sscottl#	Note: not all script variables are saved, only those that
188107543Sscottl#	      it makes sense to save.
189107543Sscottl#
190107543Sscottlsave_config() {
191107543Sscottl	echo "# Configuration file for adduser(8)."     >  ${ADDUSERCONF}
192107543Sscottl	echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF}
193109751Sfjoe	echo "# Last Modified on `${DATECMD}`."		>> ${ADDUSERCONF}
194107543Sscottl	echo ''				>> ${ADDUSERCONF}
195175719Smtm	echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF}
196112433Smtm	echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF}
197107543Sscottl	echo "defaultclass=$uclass"	>> ${ADDUSERCONF}
198107543Sscottl	echo "defaultgroups=$ugroups"	>> ${ADDUSERCONF}
199107543Sscottl	echo "passwdtype=$passwdtype" 	>> ${ADDUSERCONF}
200107543Sscottl	echo "homeprefix=$homeprefix" 	>> ${ADDUSERCONF}
201107543Sscottl	echo "defaultshell=$ushell"	>> ${ADDUSERCONF}
202107543Sscottl	echo "udotdir=$udotdir"		>> ${ADDUSERCONF}
203107543Sscottl	echo "msgfile=$msgfile"		>> ${ADDUSERCONF}
204107543Sscottl	echo "disableflag=$disableflag" >> ${ADDUSERCONF}
205168656Smtm	echo "uidstart=$uidstart"       >> ${ADDUSERCONF}
206107543Sscottl}
207107543Sscottl
208107543Sscottl# add_user
209107543Sscottl#	Add a user to the user database. If the user chose to send a welcome
210107543Sscottl#	message or lock the account, do so.
211107543Sscottl#
212107543Sscottladd_user() {
213107543Sscottl
214107543Sscottl	# Is this a configuration run? If so, don't modify user database.
215107543Sscottl	#
216107543Sscottl	if [ -n "$configflag" ]; then
217107543Sscottl		save_config
218107543Sscottl		return
219107543Sscottl	fi
220107543Sscottl
221107543Sscottl	_uid=
222107543Sscottl	_name=
223107543Sscottl	_comment=
224107543Sscottl	_gecos=
225107543Sscottl	_home=
226107543Sscottl	_group=
227107543Sscottl	_grouplist=
228107543Sscottl	_shell=
229107543Sscottl	_class=
230107543Sscottl	_dotdir=
231107543Sscottl	_expire=
232107543Sscottl	_pwexpire=
233107543Sscottl	_passwd=
234107543Sscottl	_upasswd=
235107543Sscottl	_passwdmethod=
236107543Sscottl
237109720Smtm	_name="-n '$username'"
238110595Smtm	[ -n "$uuid" ] && _uid='-u "$uuid"'
239110595Smtm	[ -n "$ulogingroup" ] && _group='-g "$ulogingroup"'
240110595Smtm	[ -n "$ugroups" ] && _grouplist='-G "$ugroups"'
241110595Smtm	[ -n "$ushell" ] && _shell='-s "$ushell"'
242110595Smtm	[ -n "$uclass" ] && _class='-L "$uclass"'
243110595Smtm	[ -n "$ugecos" ] && _comment='-c "$ugecos"'
244110595Smtm	[ -n "$udotdir" ] && _dotdir='-k "$udotdir"'
245110595Smtm	[ -n "$uexpire" ] && _expire='-e "$uexpire"'
246110595Smtm	[ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"'
247127076Smtm	if [ -z "$Dflag" -a -n "$uhome" ]; then
248127076Smtm		# The /nonexistent home directory is special. It
249127076Smtm		# means the user has no home directory.
250127076Smtm		if [ "$uhome" = "$NOHOME" ]; then
251127076Smtm			_home='-d "$uhome"'
252127076Smtm		else
253175719Smtm			# Use home directory permissions if specified
254175719Smtm			if [ -n "$uhomeperm" ]; then
255175719Smtm				_home='-m -d "$uhome" -M "$uhomeperm"'
256175719Smtm			else
257175719Smtm				_home='-m -d "$uhome"'
258175719Smtm			fi
259127076Smtm		fi
260127076Smtm	elif [ -n "$Dflag" -a -n "$uhome" ]; then
261127076Smtm		_home='-d "$uhome"'
262127076Smtm	fi
263107543Sscottl	case $passwdtype in
264107543Sscottl	no)
265107543Sscottl		_passwdmethod="-w no"
266107543Sscottl		_passwd="-h -"
267107543Sscottl		;;
268107543Sscottl	yes)
269110595Smtm		# Note on processing the password: The outer double quotes
270110595Smtm		# make literal everything except ` and \ and $.
271110595Smtm		# The outer single quotes make literal ` and $.
272110595Smtm		# We can ensure the \ isn't treated specially by specifying
273110595Smtm		# the -r switch to the read command used to obtain the input.
274110595Smtm		#
275107543Sscottl		_passwdmethod="-w yes"
276107543Sscottl		_passwd="-h 0"
277110595Smtm		_upasswd='echo "$upass" |'
278107543Sscottl		;;
279107543Sscottl	none)
280107543Sscottl		_passwdmethod="-w none"
281107543Sscottl		;;
282107543Sscottl	random)
283107543Sscottl		_passwdmethod="-w random"
284107543Sscottl		;;
285107543Sscottl	esac
286107543Sscottl
287107543Sscottl	_pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment"
288107543Sscottl	_pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"
289107543Sscottl	_pwcmd="$_pwcmd $_expire $_pwexpire"
290107543Sscottl
291107543Sscottl	if ! _output=`eval $_pwcmd` ; then
292107543Sscottl		err "There was an error adding user ($username)."
293107543Sscottl		return 1
294107543Sscottl	else
295107543Sscottl		info "Successfully added ($username) to the user database."
296107543Sscottl		if [ "random" = "$passwdtype" ]; then
297107543Sscottl			randompass="$_output"
298107543Sscottl			info "Password for ($username) is: $randompass"
299107543Sscottl		fi
300107543Sscottl	fi
301107543Sscottl
302107543Sscottl	if [ -n "$disableflag" ]; then
303107543Sscottl		if ${PWCMD} lock $username ; then
304107543Sscottl			info "Account ($username) is locked."
305107543Sscottl		else
306107543Sscottl			info "Account ($username) could NOT be locked."
307107543Sscottl		fi
308107543Sscottl	fi
309107543Sscottl
310107543Sscottl	_line=
311107543Sscottl	_owner=
312107543Sscottl	_perms=
313107543Sscottl	if [ -n "$msgflag" ]; then
314107543Sscottl		[ -r "$msgfile" ] && {
315107543Sscottl			# We're evaluating the contents of an external file.
316107543Sscottl			# Let's not open ourselves up for attack. _perms will
317107543Sscottl			# be empty if it's writeable only by the owner. _owner
318107543Sscottl			# will *NOT* be empty if the file is owned by root.
319107543Sscottl			#
320107543Sscottl			_dir="`dirname $msgfile`"
321107543Sscottl			_file="`basename $msgfile`"
322107543Sscottl			_perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune`
323107543Sscottl			_owner=`/usr/bin/find $_dir -name $_file -user 0 -prune`
324107543Sscottl			if [ -z "$_owner" -o -n "$_perms" ]; then
325107543Sscottl				err "The message file ($msgfile) may be writeable only by root."
326107543Sscottl				return 1
327107543Sscottl			fi
328107543Sscottl			cat "$msgfile" |
329107543Sscottl			while read _line ; do
330107543Sscottl				eval echo "$_line"
331107543Sscottl			done | ${MAILCMD} -s"Welcome" ${username}
332107543Sscottl			info "Sent welcome message to ($username)."
333107543Sscottl		}
334107543Sscottl	fi
335107543Sscottl}
336107543Sscottl
337107543Sscottl# get_user
338107543Sscottl#	Reads username of the account from standard input or from a global
339107543Sscottl#	variable containing an account line from a file. The username is
340107543Sscottl#	required. If this is an interactive session it will prompt in
341107543Sscottl#	a loop until a username is entered. If it is batch processing from
342107543Sscottl#	a file it will output an error message and return to the caller.
343107543Sscottl#
344107543Sscottlget_user() {
345107543Sscottl	_input=
346107543Sscottl
347107543Sscottl	# No need to take down user names if this is a configuration saving run.
348107543Sscottl	[ -n "$configflag" ] && return
349107543Sscottl
350107543Sscottl	while : ; do
351107543Sscottl		if [ -z "$fflag" ]; then
352107543Sscottl			echo -n "Username: "
353107543Sscottl			read _input
354107543Sscottl		else
355107543Sscottl			_input="`echo "$fileline" | cut -f1 -d:`"
356107543Sscottl		fi
357107543Sscottl
358167917Sle		# There *must* be a username, and it must not exist. If
359167917Sle		# this is an interactive session give the user an
360167917Sle		# opportunity to retry.
361107543Sscottl		#
362107543Sscottl		if [ -z "$_input" ]; then
363107543Sscottl			err "You must enter a username!"
364107543Sscottl			[ -z "$fflag" ] && continue
365107543Sscottl		fi
366167917Sle		${PWCMD} usershow $_input > /dev/null 2>&1
367167917Sle		if [ "$?" -eq 0 ]; then
368167917Sle			err "User exists!"
369167917Sle			[ -z "$fflag" ] && continue
370167917Sle		fi
371107543Sscottl		break
372107543Sscottl	done
373107543Sscottl	username="$_input"
374107543Sscottl}
375107543Sscottl
376107543Sscottl# get_gecos
377107543Sscottl#	Reads extra information about the user. Can be used both in interactive
378107543Sscottl#	and batch (from file) mode.
379107543Sscottl#
380107543Sscottlget_gecos() {
381107543Sscottl	_input=
382107543Sscottl
383107543Sscottl	# No need to take down additional user information for a configuration run.
384107543Sscottl	[ -n "$configflag" ] && return
385107543Sscottl
386107543Sscottl	if [ -z "$fflag" ]; then
387107543Sscottl		echo -n "Full name: "
388107543Sscottl		read _input
389107543Sscottl	else
390107543Sscottl		_input="`echo "$fileline" | cut -f7 -d:`"
391107543Sscottl	fi
392107543Sscottl	ugecos="$_input"
393107543Sscottl}
394107543Sscottl
395107543Sscottl# get_shell
396107543Sscottl#	Get the account's shell. Works in interactive and batch mode. It
397130160Smtm#	accepts either the base name of the shell or the full path.
398107543Sscottl#	If an invalid shell is entered it will simply use the default shell.
399107543Sscottl#
400107543Sscottlget_shell() {
401107543Sscottl	_input=
402107543Sscottl	_fullpath=
403107543Sscottl	ushell="$defaultshell"
404107543Sscottl
405107543Sscottl	# Make sure the current value of the shell is a valid one
406127076Smtm	if [ -z "$Sflag" ]; then
407127076Smtm		if ! shell_exists $ushell ; then
408127076Smtm			info "Using default shell ${defaultshell}."
409127076Smtm			ushell="$defaultshell"
410127076Smtm		fi
411116627Smtm	fi
412107543Sscottl
413107543Sscottl	if [ -z "$fflag" ]; then
414107543Sscottl		echo -n "Shell ($shells) [`basename $ushell`]: "
415107543Sscottl		read _input
416107543Sscottl	else
417107543Sscottl		_input="`echo "$fileline" | cut -f9 -d:`"
418107543Sscottl	fi
419107543Sscottl	if [ -n "$_input" ]; then
420127076Smtm		if [ -n "$Sflag" ]; then
421127076Smtm			ushell="$_input"
422107543Sscottl		else
423127076Smtm			_fullpath=`fullpath_from_shell $_input`
424127076Smtm			if [ -n "$_fullpath" ]; then
425127076Smtm				ushell="$_fullpath"
426127076Smtm			else
427127076Smtm				err "Invalid shell ($_input) for user $username."
428127076Smtm				info "Using default shell ${defaultshell}."
429127076Smtm				ushell="$defaultshell"
430127076Smtm			fi
431107543Sscottl		fi
432107543Sscottl	fi
433107543Sscottl}
434107543Sscottl
435107543Sscottl# get_homedir
436107543Sscottl#	Reads the account's home directory. Used both with interactive input
437107543Sscottl#	and batch input.
438107543Sscottl#
439107543Sscottlget_homedir() {
440107543Sscottl	_input=
441107543Sscottl	if [ -z "$fflag" ]; then
442107543Sscottl		echo -n "Home directory [${homeprefix}/${username}]: "
443107543Sscottl		read _input
444107543Sscottl	else
445107543Sscottl		_input="`echo "$fileline" | cut -f8 -d:`"
446107543Sscottl	fi
447107543Sscottl
448107543Sscottl	if [ -n "$_input" ]; then
449107543Sscottl		uhome="$_input"
450107543Sscottl		# if this is a configuration run, then user input is the home
451107543Sscottl		# directory prefix. Otherwise it is understood to
452107543Sscottl		# be $prefix/$user
453107543Sscottl		#
454107543Sscottl		[ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome"
455107543Sscottl	else
456107543Sscottl		uhome="${homeprefix}/${username}"
457107543Sscottl	fi
458107543Sscottl}
459107543Sscottl
460175719Smtm# get_homeperm
461175719Smtm#	Reads the account's home directory permissions.
462175719Smtm#
463175719Smtmget_homeperm() {
464175719Smtm	uhomeperm=$defaultHomePerm
465175719Smtm	_input=
466175719Smtm	_prompt=
467175719Smtm
468175719Smtm	if [ -n "$uhomeperm" ]; then
469175719Smtm		_prompt="Home directory permissions [${uhomeperm}]: "
470175719Smtm	else
471175719Smtm		_prompt="Home directory permissions (Leave empty for default): "
472175719Smtm	fi
473175719Smtm	if [ -z "$fflag" ]; then
474175719Smtm		echo -n "$_prompt"
475175719Smtm		read _input
476175719Smtm	fi
477175719Smtm
478175719Smtm	if [ -n "$_input" ]; then
479175719Smtm		uhomeperm="$_input"
480175719Smtm	fi
481175719Smtm}
482175719Smtm
483107543Sscottl# get_uid
484107543Sscottl#	Reads a numeric userid in an interactive or batch session. Automatically
485107543Sscottl#	allocates one if it is not specified.
486107543Sscottl#
487107543Sscottlget_uid() {
488168656Smtm	uuid=${uidstart}
489107543Sscottl	_input=
490107543Sscottl	_prompt=
491107543Sscottl
492107543Sscottl	if [ -n "$uuid" ]; then
493232146Sdeischen		uuid=`get_nextuid $uuid`
494107543Sscottl		_prompt="Uid [$uuid]: "
495107543Sscottl	else
496107543Sscottl		_prompt="Uid (Leave empty for default): "
497107543Sscottl	fi
498107543Sscottl	if [ -z "$fflag" ]; then
499109573Sfjoe		echo -n "$_prompt"
500107543Sscottl		read _input
501107543Sscottl	else
502107543Sscottl		_input="`echo "$fileline" | cut -f2 -d:`"
503107543Sscottl	fi
504107543Sscottl
505107543Sscottl	[ -n "$_input" ] && uuid=$_input
506107543Sscottl	uuid=`get_nextuid $uuid`
507107543Sscottl	uidstart=$uuid
508107543Sscottl}
509107543Sscottl
510107543Sscottl# get_class
511107543Sscottl#	Reads login class of account. Can be used in interactive or batch mode.
512107543Sscottl#
513107543Sscottlget_class() {
514107543Sscottl	uclass="$defaultclass"
515107543Sscottl	_input=
516107543Sscottl	_class=${uclass:-"default"}
517107543Sscottl
518107543Sscottl	if [ -z "$fflag" ]; then
519107543Sscottl		echo -n "Login class [$_class]: "
520107543Sscottl		read _input
521107543Sscottl	else
522107543Sscottl		_input="`echo "$fileline" | cut -f4 -d:`"
523107543Sscottl	fi
524107543Sscottl
525107543Sscottl	[ -n "$_input" ] && uclass="$_input"
526107543Sscottl}
527107543Sscottl
528107543Sscottl# get_logingroup
529107543Sscottl#	Reads user's login group. Can be used in both interactive and batch
530107543Sscottl#	modes. The specified value can be a group name or its numeric id.
531112433Smtm#	This routine leaves the field blank if nothing is provided and
532112433Smtm#	a default login group has not been set. The pw(8) command
533112433Smtm#	will then provide a login group with the same name as the username.
534107543Sscottl#
535107543Sscottlget_logingroup() {
536112433Smtm	ulogingroup="$defaultLgroup"
537107543Sscottl	_input=
538107543Sscottl
539107543Sscottl	if [ -z "$fflag" ]; then
540112433Smtm		echo -n "Login group [${ulogingroup:-$username}]: "
541107543Sscottl		read _input
542107543Sscottl	else
543107543Sscottl		_input="`echo "$fileline" | cut -f3 -d:`"
544107543Sscottl	fi
545107543Sscottl
546107543Sscottl	# Pw(8) will use the username as login group if it's left empty
547112433Smtm	[ -n "$_input" ] && ulogingroup="$_input"
548107543Sscottl}
549107543Sscottl
550107543Sscottl# get_groups
551107543Sscottl#	Read additional groups for the user. It can be used in both interactive
552107543Sscottl#	and batch modes.
553107543Sscottl#
554107543Sscottlget_groups() {
555107543Sscottl	ugroups="$defaultgroups"
556107543Sscottl	_input=
557107543Sscottl	_group=${ulogingroup:-"${username}"}
558107543Sscottl
559107543Sscottl	if [ -z "$configflag" ]; then
560107543Sscottl		[ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username"
561107543Sscottl		[ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: "
562107543Sscottl	else
563107543Sscottl		[ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: "
564107543Sscottl	fi
565107543Sscottl	read _input
566107543Sscottl
567107543Sscottl	[ -n "$_input" ] && ugroups="$_input"
568107543Sscottl}
569107543Sscottl
570107543Sscottl# get_expire_dates
571107543Sscottl#	Read expiry information for the account and also for the password. This
572107543Sscottl#	routine is used only from batch processing mode.
573107543Sscottl#
574107543Sscottlget_expire_dates() {
575107543Sscottl	upwexpire="`echo "$fileline" | cut -f5 -d:`"
576107543Sscottl	uexpire="`echo "$fileline" | cut -f6 -d:`"
577107543Sscottl}
578107543Sscottl
579107543Sscottl# get_password
580107543Sscottl#	Read the password in batch processing mode. The password field matters
581107543Sscottl#	only when the password type is "yes" or "random". If the field is empty and the
582107543Sscottl#	password type is "yes", then it assumes the account has an empty passsword
583107543Sscottl#	and changes the password type accordingly. If the password type is "random"
584107543Sscottl#	and the password field is NOT empty, then it assumes the account will NOT
585107543Sscottl#	have a random password and set passwdtype to "yes."
586107543Sscottl#
587107543Sscottlget_password() {
588107543Sscottl	# We may temporarily change a password type. Make sure it's changed
589107543Sscottl	# back to whatever it was before we process the next account.
590107543Sscottl	#
591107543Sscottl	[ -n "$savedpwtype" ] && {
592107543Sscottl		passwdtype=$savedpwtype
593107543Sscottl		savedpwtype=
594107543Sscottl	}
595107543Sscottl
596107543Sscottl	# There may be a ':' in the password
597107543Sscottl	upass=${fileline#*:*:*:*:*:*:*:*:*:}
598107543Sscottl
599107543Sscottl	if [ -z "$upass" ]; then
600107543Sscottl		case $passwdtype in
601107543Sscottl		yes)
602107543Sscottl			# if it's empty, assume an empty password
603107543Sscottl			passwdtype=none
604107543Sscottl			savedpwtype=yes
605107543Sscottl			;;
606107543Sscottl		esac
607107543Sscottl	else
608107543Sscottl		case $passwdtype in
609107543Sscottl		random)
610107543Sscottl			passwdtype=yes
611107543Sscottl			savedpwtype=random
612107543Sscottl			;;
613107543Sscottl		esac
614107543Sscottl	fi
615107543Sscottl}
616107543Sscottl
617107543Sscottl# input_from_file
618107543Sscottl#	Reads a line of account information from standard input and
619107543Sscottl#	adds it to the user database.
620107543Sscottl#
621107543Sscottlinput_from_file() {
622107543Sscottl	_field=
623107543Sscottl
624110595Smtm	while read -r fileline ; do
625107543Sscottl		case "$fileline" in
626107543Sscottl		\#*|'')
627107543Sscottl			;;
628168651Smtm		*)
629168651Smtm			get_user || continue
630168651Smtm			get_gecos
631168651Smtm			get_uid
632168651Smtm			get_logingroup
633168651Smtm			get_class
634168651Smtm			get_shell
635168651Smtm			get_homedir
636175719Smtm			get_homeperm
637168651Smtm			get_password
638168651Smtm			get_expire_dates
639172823Smtm			ugroups="$defaultgroups"
640168651Smtm
641168651Smtm			add_user
642168651Smtm			;;
643107543Sscottl		esac
644107543Sscottl	done
645107543Sscottl}
646107543Sscottl
647107543Sscottl# input_interactive
648107543Sscottl#	Prompts for user information interactively, and commits to
649107543Sscottl#	the user database.
650107543Sscottl#
651107543Sscottlinput_interactive() {
652107543Sscottl	_disable=
653107543Sscottl	_pass=
654107543Sscottl	_passconfirm=
655107543Sscottl	_random="no"
656107543Sscottl	_emptypass="no"
657107543Sscottl	_usepass="yes"
658112401Smtm	_logingroup_ok="no"
659112401Smtm	_groups_ok="no"
660107543Sscottl	case $passwdtype in
661107543Sscottl	none)
662107543Sscottl		_emptypass="yes"
663107543Sscottl		_usepass="yes"
664107543Sscottl		;;
665107543Sscottl	no)
666107543Sscottl		_usepass="no"
667107543Sscottl		;;
668107543Sscottl	random)
669107543Sscottl		_random="yes"
670107543Sscottl		;;
671107543Sscottl	esac
672107543Sscottl
673107543Sscottl	get_user
674107543Sscottl	get_gecos
675107543Sscottl	get_uid
676110537Sadrian
677110537Sadrian	# The case where group = user is handled elsewhere, so
678110537Sadrian	# validate any other groups the user is invited to.
679110537Sadrian	until [ "$_logingroup_ok" = yes ]; do
680110537Sadrian		get_logingroup
681110537Sadrian		_logingroup_ok=yes
682110537Sadrian		if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then
683110537Sadrian			if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then
684110537Sadrian				echo "Group $ulogingroup does not exist!"
685110537Sadrian				_logingroup_ok=no
686110537Sadrian			fi
687110537Sadrian		fi
688110537Sadrian	done
689110537Sadrian	until [ "$_groups_ok" = yes ]; do
690110537Sadrian		get_groups
691110537Sadrian		_groups_ok=yes
692110537Sadrian		for i in $ugroups; do
693110537Sadrian			if [ "$username" != "$i" ]; then
694110537Sadrian				if ! ${PWCMD} show group $i > /dev/null 2>&1; then
695110537Sadrian					echo "Group $i does not exist!"
696110537Sadrian					_groups_ok=no
697110537Sadrian				fi
698110537Sadrian			fi
699110537Sadrian		done
700110537Sadrian	done
701110537Sadrian
702107543Sscottl	get_class
703107543Sscottl	get_shell
704107543Sscottl	get_homedir
705175719Smtm	get_homeperm
706107543Sscottl
707107543Sscottl	while : ; do
708107543Sscottl		echo -n "Use password-based authentication? [$_usepass]: "
709107543Sscottl		read _input
710107543Sscottl		[ -z "$_input" ] && _input=$_usepass
711107543Sscottl		case $_input in
712107543Sscottl		[Nn][Oo]|[Nn])
713107543Sscottl			passwdtype="no"
714107543Sscottl			;;
715107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
716107543Sscottl			while : ; do
717107543Sscottl				echo -n "Use an empty password? (yes/no) [$_emptypass]: "
718107543Sscottl				read _input
719107543Sscottl				[ -n "$_input" ] && _emptypass=$_input
720107543Sscottl				case $_emptypass in
721107543Sscottl				[Nn][Oo]|[Nn])
722107543Sscottl					echo -n "Use a random password? (yes/no) [$_random]: "
723107543Sscottl					read _input
724107543Sscottl					[ -n "$_input" ] && _random="$_input"
725107543Sscottl					case $_random in
726107543Sscottl					[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
727107543Sscottl						passwdtype="random"
728107543Sscottl						break
729107543Sscottl						;;
730107543Sscottl					esac
731107543Sscottl					passwdtype="yes"
732112401Smtm					[ -n "$configflag" ] && break
733107543Sscottl					trap 'stty echo; exit' 0 1 2 3 15
734107543Sscottl					stty -echo
735107543Sscottl					echo -n "Enter password: "
736360390Skevans					IFS= read -r upass
737107543Sscottl					echo''
738107543Sscottl					echo -n "Enter password again: "
739360390Skevans					IFS= read -r _passconfirm
740107543Sscottl					echo ''
741107543Sscottl					stty echo
742107543Sscottl					# if user entered a blank password
743107543Sscottl					# explicitly ask again.
744107543Sscottl					[ -z "$upass" -a -z "$_passconfirm" ] \
745107543Sscottl					    && continue
746107543Sscottl					;;
747107543Sscottl				[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
748107543Sscottl					passwdtype="none"
749107543Sscottl					break;
750107543Sscottl					;;
751107543Sscottl				*)
752107543Sscottl					# invalid answer; repeat the loop
753107543Sscottl					continue
754107543Sscottl					;;
755107543Sscottl				esac
756107543Sscottl				if [ "$upass" != "$_passconfirm" ]; then
757107543Sscottl					echo "Passwords did not match!"
758107543Sscottl					continue
759107543Sscottl				fi
760107543Sscottl				break
761107543Sscottl			done
762107543Sscottl			;;
763107543Sscottl		*)
764107543Sscottl			# invalid answer; repeat loop
765107543Sscottl			continue
766107543Sscottl			;;
767107543Sscottl		esac
768107543Sscottl		break;
769107543Sscottl	done
770107543Sscottl	_disable=${disableflag:-"no"}
771107543Sscottl	while : ; do
772107543Sscottl		echo -n "Lock out the account after creation? [$_disable]: "
773107543Sscottl		read _input
774107543Sscottl		[ -z "$_input" ] && _input=$_disable
775107543Sscottl		case $_input in
776107543Sscottl		[Nn][Oo]|[Nn])
777107543Sscottl			disableflag=
778107543Sscottl			;;
779107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
780107543Sscottl			disableflag=yes
781107543Sscottl			;;
782107543Sscottl		*)
783107543Sscottl			# invalid answer; repeat loop
784107543Sscottl			continue
785107543Sscottl			;;
786107543Sscottl		esac
787107543Sscottl		break
788107543Sscottl	done
789107543Sscottl	
790107543Sscottl	# Display the information we have so far and prompt to
791107543Sscottl	# commit it.
792107543Sscottl	#
793107543Sscottl	_disable=${disableflag:-"no"}
794107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" Username $username
795107543Sscottl	case $passwdtype in
796107543Sscottl	yes)
797107543Sscottl		_pass='*****'
798107543Sscottl		;;
799107543Sscottl	no)
800107543Sscottl		_pass='<disabled>'
801107543Sscottl		;;
802107543Sscottl	none)
803107543Sscottl		_pass='<blank>'
804107543Sscottl		;;
805107543Sscottl	random)
806107543Sscottl		_pass='<random>'
807107543Sscottl		;;
808107543Sscottl	esac
809109751Sfjoe	[ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass"
810109751Sfjoe	[ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype"
811107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos"
812107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid"
813107543Sscottl	printf "%-10s : %s\n" "Class" "$uclass"
814112433Smtm	printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups"
815107543Sscottl	printf "%-10s : %s\n" "Home" "$uhome"
816175719Smtm	printf "%-10s : %s\n" "Home Mode" "$uhomeperm"
817107543Sscottl	printf "%-10s : %s\n" "Shell" "$ushell"
818107543Sscottl	printf "%-10s : %s\n" "Locked" "$_disable"
819107543Sscottl	while : ; do
820107543Sscottl		echo -n "OK? (yes/no): "
821107543Sscottl		read _input
822107543Sscottl		case $_input in
823107543Sscottl		[Nn][Oo]|[Nn])
824107543Sscottl			return 1
825107543Sscottl			;;
826107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
827107543Sscottl			add_user
828107543Sscottl			;;
829107543Sscottl		*)
830107543Sscottl			continue
831107543Sscottl			;;
832107543Sscottl		esac
833107543Sscottl		break
834107543Sscottl	done
835107543Sscottl	return 0
836107543Sscottl}
837107543Sscottl
838145618Srobert#### END SUBROUTINE DEFINITION ####
839107543Sscottl
840107543SscottlTHISCMD=`/usr/bin/basename $0`
841107543SscottlDEFAULTSHELL=/bin/sh
842107543SscottlADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
843107543SscottlPWCMD="${PWCMD:-/usr/sbin/pw}"
844107543SscottlMAILCMD="${MAILCMD:-mail}"
845107543SscottlETCSHELLS="${ETCSHELLS:-/etc/shells}"
846127076SmtmNOHOME="/nonexistent"
847116627SmtmNOLOGIN="nologin"
848127635ScpercivaNOLOGIN_PATH="/usr/sbin/nologin"
849109751SfjoeGREPCMD="/usr/bin/grep"
850109751SfjoeDATECMD="/bin/date"
851107543Sscottl
852107543Sscottl# Set default values
853107543Sscottl#
854107543Sscottlusername=
855107543Sscottluuid=
856107543Sscottluidstart=
857107543Sscottlugecos=
858107543Sscottlulogingroup=
859107543Sscottluclass=
860107543Sscottluhome=
861175719Smtmuhomeperm=
862107543Sscottlupass=
863107543Sscottlushell=
864107543Sscottludotdir=/usr/share/skel
865107543Sscottlugroups=
866107543Sscottluexpire=
867107543Sscottlupwexpire=
868107543Sscottlshells="`valid_shells`"
869107543Sscottlpasswdtype="yes"
870107543Sscottlmsgfile=/etc/adduser.msg
871107543Sscottlmsgflag=
872107543Sscottlquietflag=
873107543Sscottlconfigflag=
874107543Sscottlfflag=
875107543Sscottlinfile=
876107543Sscottldisableflag=
877127076SmtmDflag=
878127076SmtmSflag=
879107543Sscottlreadconfig="yes"
880107543Sscottlhomeprefix="/home"
881107543Sscottlrandompass=
882107543Sscottlfileline=
883107543Sscottlsavedpwtype=
884107543Sscottldefaultclass=
885112433SmtmdefaultLgroup=
886116784Smtmdefaultgroups=
887107543Sscottldefaultshell="${DEFAULTSHELL}"
888175719SmtmdefaultHomePerm=
889107543Sscottl
890107543Sscottl# Make sure the user running this program is root. This isn't a security
891202312Sjoel# measure as much as it is a useful method of reminding the user to
892107543Sscottl# 'su -' before he/she wastes time entering data that won't be saved.
893107543Sscottl#
894107543Sscottlprocowner=${procowner:-`/usr/bin/id -u`}
895107543Sscottlif [ "$procowner" != "0" ]; then
896107543Sscottl	err 'you must be the super-user (uid 0) to use this utility.'
897107543Sscottl	exit 1
898107543Sscottlfi
899107543Sscottl
900228990Suqs# Override from our conf file
901107543Sscottl# Quickly go through the commandline line to see if we should read
902107543Sscottl# from our configuration file. The actual parsing of the commandline
903107543Sscottl# arguments happens after we read in our configuration file (commandline
904107543Sscottl# should override configuration file).
905107543Sscottl#
906107543Sscottlfor _i in $* ; do
907107543Sscottl	if [ "$_i" = "-N" ]; then
908107543Sscottl		readconfig=
909107543Sscottl		break;
910107543Sscottl	fi
911107543Sscottldone
912107543Sscottlif [ -n "$readconfig" ]; then
913107543Sscottl	# On a long-lived system, the first time this script is run it
914107543Sscottl	# will barf upon reading the configuration file for its perl predecessor.
915107543Sscottl	if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then
916107543Sscottl		[ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1
917107543Sscottl	fi
918107543Sscottlfi 
919107543Sscottl
920228990Suqs# Process command-line options
921107543Sscottl#
922107543Sscottlfor _switch ; do
923107543Sscottl	case $_switch in
924107543Sscottl	-L)
925107543Sscottl		defaultclass="$2"
926107543Sscottl		shift; shift
927107543Sscottl		;;
928107543Sscottl	-C)
929107543Sscottl		configflag=yes
930107543Sscottl		shift
931107543Sscottl		;;
932127076Smtm	-D)
933127076Smtm		Dflag=yes
934127076Smtm		shift
935127076Smtm		;;
936107543Sscottl	-E)
937107543Sscottl		disableflag=yes
938107543Sscottl		shift
939107543Sscottl		;;
940107543Sscottl	-k)
941107543Sscottl		udotdir="$2"
942107543Sscottl		shift; shift
943107543Sscottl		;;
944107543Sscottl	-f)
945107543Sscottl		[ "$2" != "-" ] && infile="$2"
946107543Sscottl		fflag=yes
947107543Sscottl		shift; shift
948107543Sscottl		;;
949112433Smtm	-g)
950112433Smtm		defaultLgroup="$2"
951112433Smtm		shift; shift
952112433Smtm		;;
953107543Sscottl	-G)
954107543Sscottl		defaultgroups="$2"
955107543Sscottl		shift; shift
956107543Sscottl		;;
957107543Sscottl	-h)
958107543Sscottl		show_usage
959107543Sscottl		exit 0
960107543Sscottl		;;
961107543Sscottl	-d)
962107543Sscottl		homeprefix="$2"
963107543Sscottl		shift; shift
964107543Sscottl		;;
965107543Sscottl	-m)
966107543Sscottl		case "$2" in
967107543Sscottl		[Nn][Oo])
968107543Sscottl			msgflag=
969107543Sscottl			;;
970107543Sscottl		*)
971107543Sscottl			msgflag=yes
972107543Sscottl			msgfile="$2"
973107543Sscottl			;;
974107543Sscottl		esac
975107543Sscottl		shift; shift
976107543Sscottl		;;
977175719Smtm	-M)
978175719Smtm		defaultHomePerm=$2
979175719Smtm		shift; shift
980175719Smtm		;;
981107543Sscottl	-N)
982107543Sscottl		readconfig=
983107543Sscottl		shift
984107543Sscottl		;;
985107543Sscottl	-w)
986107543Sscottl		case "$2" in
987107543Sscottl		no|none|random|yes)
988107543Sscottl			passwdtype=$2
989107543Sscottl			;;
990107543Sscottl		*)
991107543Sscottl			show_usage
992107543Sscottl			exit 1
993107543Sscottl			;;
994107543Sscottl		esac
995107543Sscottl		shift; shift
996107543Sscottl		;;
997107543Sscottl	-q)
998107543Sscottl		quietflag=yes
999107543Sscottl		shift
1000107543Sscottl		;;
1001107543Sscottl	-s)
1002107543Sscottl		defaultshell="`fullpath_from_shell $2`"
1003107543Sscottl		shift; shift
1004107543Sscottl		;;
1005127076Smtm	-S)
1006127076Smtm		Sflag=yes
1007127076Smtm		shift
1008127076Smtm		;;
1009107543Sscottl	-u)
1010107543Sscottl		uidstart=$2
1011107543Sscottl		shift; shift
1012107543Sscottl		;;
1013107543Sscottl	esac
1014107543Sscottldone
1015107543Sscottl
1016107543Sscottl# If the -f switch was used, get input from a file. Otherwise,
1017107543Sscottl# this is an interactive session.
1018107543Sscottl#
1019107543Sscottlif [ -n "$fflag" ]; then
1020107543Sscottl	if [ -z "$infile" ]; then
1021107543Sscottl		input_from_file
1022107543Sscottl	elif [ -n "$infile" ]; then
1023107543Sscottl		if [ -r "$infile" ]; then
1024107543Sscottl			input_from_file < $infile
1025107543Sscottl		else
1026107543Sscottl			err "File ($infile) is unreadable or does not exist."
1027107543Sscottl		fi
1028107543Sscottl	fi
1029107543Sscottlelse
1030107543Sscottl	input_interactive
1031109768Smtm	while : ; do
1032112401Smtm		if [ -z "$configflag" ]; then
1033112401Smtm			echo -n "Add another user? (yes/no): "
1034112401Smtm		else
1035112401Smtm			echo -n "Re-edit the default configuration? (yes/no): "
1036112401Smtm		fi
1037109768Smtm		read _input
1038109768Smtm		case $_input in
1039109768Smtm		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
1040109768Smtm			uidstart=`get_nextuid $uidstart`
1041109768Smtm			input_interactive
1042109768Smtm			continue
1043109768Smtm			;;
1044109768Smtm		[Nn][Oo]|[Nn])
1045109768Smtm			echo "Goodbye!"
1046109768Smtm			;;
1047109768Smtm		*)
1048109768Smtm			continue
1049109768Smtm			;;
1050109768Smtm		esac
1051109768Smtm		break
1052109768Smtm	done
1053107543Sscottlfi
1054