adduser.sh revision 112433
1107543Sscottl#!/bin/sh
2107543Sscottl#
3127076Smtm# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
4107543Sscottl#
5107543Sscottl# Redistribution and use in source and binary forms, with or without
6107543Sscottl# modification, are permitted provided that the following conditions
7107543Sscottl# are met:
8107543Sscottl# 1. Redistributions of source code must retain the above copyright
9107543Sscottl#    notice, this list of conditions and the following disclaimer.
10107543Sscottl# 2. Redistributions in binary form must reproduce the above copyright
11107543Sscottl#    notice, this list of conditions and the following disclaimer in the
12107543Sscottl#    documentation and/or other materials provided with the distribution.
13107543Sscottl#
14107543Sscottl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15107543Sscottl# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16107543Sscottl# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17107543Sscottl# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18107543Sscottl# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19107543Sscottl# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20107543Sscottl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21107543Sscottl# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22107543Sscottl# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23107543Sscottl# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24107543Sscottl#
25116624Smtm#       Email: Mike Makonnen <mtm@identd.net>
26107543Sscottl#
27107543Sscottl# $FreeBSD: head/usr.sbin/adduser/adduser.sh 112433 2003-03-20 16:36:03Z mtm $
28107543Sscottl#
29107543Sscottl
30107543Sscottl# err msg
31107543Sscottl#       Display $msg on stderr, unless we're being quiet.
32107543Sscottl# 
33107543Sscottlerr() {
34107543Sscottl	if [ -z "$quietflag" ]; then
35107543Sscottl        	echo 1>&2 ${THISCMD}: ERROR: $*
36107543Sscottl	fi
37107543Sscottl}
38107543Sscottl
39107543Sscottl# info msg
40107543Sscottl#       Display $msg on stdout, unless we're being quiet.
41107543Sscottl# 
42107543Sscottlinfo() {
43107543Sscottl	if [ -z "$quietflag" ]; then
44107543Sscottl        	echo ${THISCMD}: INFO: $*
45107543Sscottl	fi
46107543Sscottl}
47107543Sscottl
48107543Sscottl# get_nextuid
49107543Sscottl#	Output the value of $_uid if it is available for use. If it
50107543Sscottl#	is not, output the value of the next higher uid that is available.
51107543Sscottl#	If a uid is not specified, output the first available uid, as indicated
52107543Sscottl#	by pw(8).
53107543Sscottl# 
54107543Sscottlget_nextuid () {
55107543Sscottl	_uid=$1
56107543Sscottl	_nextuid=
57107543Sscottl
58107543Sscottl	if [ -z "$_uid" ]; then
59107543Sscottl		_nextuid="`${PWCMD} usernext | cut -f1 -d:`"
60107543Sscottl	else
61107543Sscottl		while : ; do
62107543Sscottl			${PWCMD} usershow $_uid > /dev/null 2>&1
63107543Sscottl			if [ ! "$?" -eq 0 ]; then
64107543Sscottl				_nextuid=$_uid
65107543Sscottl				break
66107543Sscottl			fi
67107543Sscottl			_uid=$(($_uid + 1))
68107543Sscottl		done
69107543Sscottl	fi
70107543Sscottl	echo $_nextuid
71107543Sscottl}
72107543Sscottl
73107543Sscottl# show_usage
74107543Sscottl#	Display usage information for this utility.
75107543Sscottl#
76107543Sscottlshow_usage() {
77107543Sscottl	echo "usage: ${THISCMD} [options]"
78107543Sscottl	echo "  options may include:"
79107543Sscottl	echo "  -C		save to the configuration file only"
80127076Smtm	echo "  -E		disable this account after creation"
81107543Sscottl	echo "  -G		additional groups to add accounts to"
82107543Sscottl	echo "  -L		login class of the user"
83107543Sscottl	echo "  -N		do not read configuration file"
84107543Sscottl	echo "  -d		home directory"
85127076Smtm	echo "  -f		file from which input will be received"
86107543Sscottl	echo "  -h		display this usage message"
87107543Sscottl	echo "  -k		path to skeleton home directory"
88112519Smtm	echo "  -m		user welcome message file"
89107543Sscottl	echo "  -q		absolute minimal user feedback"
90107543Sscottl	echo "  -s		shell"
91107543Sscottl	echo "  -u		uid to start at"
92107543Sscottl	echo "  -w		password type: no, none, yes or random"
93107543Sscottl}
94107543Sscottl
95107543Sscottl# valid_shells
96107543Sscottl#	Outputs a list of valid shells from /etc/shells. Only the
97107543Sscottl#	basename of the shell is output.
98107543Sscottl#
99107543Sscottlvalid_shells() {
100107543Sscottl	_prefix=
101107543Sscottl	cat ${ETCSHELLS} |
102107543Sscottl	while read _path _junk ; do
103107543Sscottl		case $_path in
104107543Sscottl		\#*|'')
105107543Sscottl			;;
106107543Sscottl		*)
107107543Sscottl			echo -n "${_prefix}`basename $_path`"
108107543Sscottl			_prefix=' '
109107543Sscottl			;;
110107543Sscottl		esac
111107543Sscottl	done
112107543Sscottl}
113107543Sscottl
114107543Sscottl# fullpath_from_shell shell
115116627Smtm#	Given $shell, the basename component of a valid shell, get the
116127635Scperciva#	full path to the shell from the /etc/shells file.
117116627Smtm#
118107543Sscottlfullpath_from_shell() {
119107543Sscottl	_shell=$1
120107543Sscottl	[ -z "$_shell" ] && return 1
121130160Smtm
122130160Smtm	cat ${ETCSHELLS} |
123107543Sscottl	while read _path _junk ; do
124107543Sscottl		case "$_path" in
125107543Sscottl		\#*|'')
126107543Sscottl			;;
127107543Sscottl		*)
128107543Sscottl			if [ "`basename $_path`" = "$_shell" ]; then
129135616Sroam				echo $_path
130135616Sroam				return 0
131135616Sroam			fi
132135616Sroam			;;
133135616Sroam		esac
134135616Sroam	done
135135616Sroam	return 1
136135616Sroam}
137135616Sroam
138135616Sroam# save_config
139135616Sroam#	Save some variables to a configuration file.
140107543Sscottl#	Note: not all script variables are saved, only those that
141107543Sscottl#	      it makes sense to save.
142107543Sscottl#
143107543Sscottlsave_config() {
144107543Sscottl	echo "# Configuration file for adduser(8)."     >  ${ADDUSERCONF}
145107543Sscottl	echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF}
146130160Smtm	echo "# Last Modified on `${DATECMD}`."		>> ${ADDUSERCONF}
147130160Smtm	echo ''				>> ${ADDUSERCONF}
148107543Sscottl	echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF}
149107543Sscottl	echo "defaultclass=$uclass"	>> ${ADDUSERCONF}
150107543Sscottl	echo "defaultgroups=$ugroups"	>> ${ADDUSERCONF}
151107543Sscottl	echo "passwdtype=$passwdtype" 	>> ${ADDUSERCONF}
152107543Sscottl	echo "homeprefix=$homeprefix" 	>> ${ADDUSERCONF}
153107543Sscottl	echo "defaultshell=$ushell"	>> ${ADDUSERCONF}
154116627Smtm	echo "udotdir=$udotdir"		>> ${ADDUSERCONF}
155107543Sscottl	echo "msgfile=$msgfile"		>> ${ADDUSERCONF}
156107543Sscottl	echo "disableflag=$disableflag" >> ${ADDUSERCONF}
157107543Sscottl}
158116627Smtm
159116627Smtm# add_user
160116627Smtm#	Add a user to the user database. If the user chose to send a welcome
161116627Smtm#	message or lock the account, do so.
162116627Smtm#
163116627Smtmadd_user() {
164116627Smtm
165116627Smtm	# Is this a configuration run? If so, don't modify user database.
166116627Smtm	#
167116627Smtm	if [ -n "$configflag" ]; then
168116627Smtm		save_config
169116627Smtm		return
170116627Smtm	fi
171116627Smtm
172116627Smtm	_uid=
173116627Smtm	_name=
174116627Smtm	_comment=
175116627Smtm	_gecos=
176116627Smtm	_home=
177116627Smtm	_group=
178116628Smtm	_grouplist=
179116627Smtm	_shell=
180116627Smtm	_class=
181116627Smtm	_dotdir=
182116627Smtm	_expire=
183107543Sscottl	_pwexpire=
184107543Sscottl	_passwd=
185107543Sscottl	_upasswd=
186107543Sscottl	_passwdmethod=
187107543Sscottl
188107543Sscottl	_name="-n '$username'"
189107543Sscottl	[ -n "$uuid" ] && _uid='-u "$uuid"'
190107543Sscottl	[ -n "$ulogingroup" ] && _group='-g "$ulogingroup"'
191109751Sfjoe	[ -n "$ugroups" ] && _grouplist='-G "$ugroups"'
192107543Sscottl	[ -n "$ushell" ] && _shell='-s "$ushell"'
193112433Smtm	[ -n "$uhome" ] && _home='-m -d "$uhome"'
194107543Sscottl	[ -n "$uclass" ] && _class='-L "$uclass"'
195107543Sscottl	[ -n "$ugecos" ] && _comment='-c "$ugecos"'
196107543Sscottl	[ -n "$udotdir" ] && _dotdir='-k "$udotdir"'
197107543Sscottl	[ -n "$uexpire" ] && _expire='-e "$uexpire"'
198107543Sscottl	[ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"'
199107543Sscottl	case $passwdtype in
200107543Sscottl	no)
201107543Sscottl		_passwdmethod="-w no"
202107543Sscottl		_passwd="-h -"
203107543Sscottl		;;
204107543Sscottl	yes)
205107543Sscottl		# Note on processing the password: The outer double quotes
206107543Sscottl		# make literal everything except ` and \ and $.
207107543Sscottl		# The outer single quotes make literal ` and $.
208107543Sscottl		# We can ensure the \ isn't treated specially by specifying
209107543Sscottl		# the -r switch to the read command used to obtain the input.
210107543Sscottl		#
211107543Sscottl		_passwdmethod="-w yes"
212107543Sscottl		_passwd="-h 0"
213107543Sscottl		_upasswd='echo "$upass" |'
214107543Sscottl		;;
215107543Sscottl	none)
216107543Sscottl		_passwdmethod="-w none"
217107543Sscottl		;;
218107543Sscottl	random)
219107543Sscottl		_passwdmethod="-w random"
220107543Sscottl		;;
221107543Sscottl	esac
222107543Sscottl
223107543Sscottl	_pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment"
224107543Sscottl	_pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"
225107543Sscottl	_pwcmd="$_pwcmd $_expire $_pwexpire"
226107543Sscottl
227107543Sscottl	if ! _output=`eval $_pwcmd` ; then
228107543Sscottl		err "There was an error adding user ($username)."
229107543Sscottl		return 1
230107543Sscottl	else
231107543Sscottl		info "Successfully added ($username) to the user database."
232107543Sscottl		if [ "random" = "$passwdtype" ]; then
233109720Smtm			randompass="$_output"
234110595Smtm			info "Password for ($username) is: $randompass"
235110595Smtm		fi
236110595Smtm	fi
237110595Smtm
238110595Smtm	if [ -n "$disableflag" ]; then
239110595Smtm		if ${PWCMD} lock $username ; then
240110595Smtm			info "Account ($username) is locked."
241110595Smtm		else
242110595Smtm			info "Account ($username) could NOT be locked."
243127076Smtm		fi
244127076Smtm	fi
245127076Smtm
246127076Smtm	_line=
247127076Smtm	_owner=
248127076Smtm	_perms=
249127076Smtm	if [ -n "$msgflag" ]; then
250127076Smtm		[ -r "$msgfile" ] && {
251127076Smtm			# We're evaluating the contents of an external file.
252127076Smtm			# Let's not open ourselves up for attack. _perms will
253127076Smtm			# be empty if it's writeable only by the owner. _owner
254107543Sscottl			# will *NOT* be empty if the file is owned by root.
255107543Sscottl			#
256107543Sscottl			_dir="`dirname $msgfile`"
257107543Sscottl			_file="`basename $msgfile`"
258107543Sscottl			_perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune`
259107543Sscottl			_owner=`/usr/bin/find $_dir -name $_file -user 0 -prune`
260110595Smtm			if [ -z "$_owner" -o -n "$_perms" ]; then
261110595Smtm				err "The message file ($msgfile) may be writeable only by root."
262110595Smtm				return 1
263110595Smtm			fi
264110595Smtm			cat "$msgfile" |
265110595Smtm			while read _line ; do
266107543Sscottl				eval echo "$_line"
267107543Sscottl			done | ${MAILCMD} -s"Welcome" ${username}
268110595Smtm			info "Sent welcome message to ($username)."
269107543Sscottl		}
270107543Sscottl	fi
271107543Sscottl}
272107543Sscottl
273107543Sscottl# get_user
274107543Sscottl#	Reads username of the account from standard input or from a global
275107543Sscottl#	variable containing an account line from a file. The username is
276107543Sscottl#	required. If this is an interactive session it will prompt in
277107543Sscottl#	a loop until a username is entered. If it is batch processing from
278107543Sscottl#	a file it will output an error message and return to the caller.
279107543Sscottl#
280107543Sscottlget_user() {
281107543Sscottl	_input=
282107543Sscottl
283107543Sscottl	# No need to take down user names if this is a configuration saving run.
284107543Sscottl	[ -n "$configflag" ] && return
285107543Sscottl
286107543Sscottl	while : ; do
287107543Sscottl		if [ -z "$fflag" ]; then
288107543Sscottl			echo -n "Username: "
289107543Sscottl			read _input
290107543Sscottl		else
291107543Sscottl			_input="`echo "$fileline" | cut -f1 -d:`"
292107543Sscottl		fi
293107543Sscottl
294107543Sscottl		# There *must* be a username. If this is an interactive
295107543Sscottl		# session give the user an opportunity to retry.
296107543Sscottl		#
297107543Sscottl		if [ -z "$_input" ]; then
298107543Sscottl			err "You must enter a username!"
299107543Sscottl			[ -z "$fflag" ] && continue
300107543Sscottl		fi
301107543Sscottl		break
302107543Sscottl	done
303107543Sscottl	username="$_input"
304107543Sscottl}
305107543Sscottl
306107543Sscottl# get_gecos
307107543Sscottl#	Reads extra information about the user. Can be used both in interactive
308107543Sscottl#	and batch (from file) mode.
309107543Sscottl#
310107543Sscottlget_gecos() {
311107543Sscottl	_input=
312107543Sscottl
313107543Sscottl	# No need to take down additional user information for a configuration run.
314107543Sscottl	[ -n "$configflag" ] && return
315107543Sscottl
316107543Sscottl	if [ -z "$fflag" ]; then
317107543Sscottl		echo -n "Full name: "
318107543Sscottl		read _input
319107543Sscottl	else
320107543Sscottl		_input="`echo "$fileline" | cut -f7 -d:`"
321107543Sscottl	fi
322107543Sscottl	ugecos="$_input"
323107543Sscottl}
324107543Sscottl
325107543Sscottl# get_shell
326107543Sscottl#	Get the account's shell. Works in interactive and batch mode. It
327107543Sscottl#	accepts only the base name of the shell, NOT the full path.
328107543Sscottl#	If an invalid shell is entered it will simply use the default shell.
329107543Sscottl#
330107543Sscottlget_shell() {
331107543Sscottl	_input=
332107543Sscottl	_fullpath=
333107543Sscottl	ushell="$defaultshell"
334107543Sscottl
335107543Sscottl	# Make sure the current value of the shell is a valid one
336107543Sscottl	_shellchk="${GREPCMD} '^$ushell$' ${ETCSHELLS} > /dev/null 2>&1"
337107543Sscottl	eval $_shellchk || {
338107543Sscottl		err "Invalid shell ($ushell). Using default shell ${defaultshell}."
339107543Sscottl		ushell="$defaultshell"
340107543Sscottl	}
341107543Sscottl
342107543Sscottl	if [ -z "$fflag" ]; then
343107543Sscottl		echo -n "Shell ($shells) [`basename $ushell`]: "
344107543Sscottl		read _input
345107543Sscottl	else
346107543Sscottl		_input="`echo "$fileline" | cut -f9 -d:`"
347107543Sscottl	fi
348107543Sscottl	if [ -n "$_input" ]; then
349107543Sscottl		_fullpath=`fullpath_from_shell $_input`
350107543Sscottl		if [ -n "$_fullpath" ]; then
351107543Sscottl			ushell="$_fullpath"
352107543Sscottl		else
353107543Sscottl			err "Invalid shell selection. Using default shell ${defaultshell}."
354107543Sscottl			ushell="$defaultshell"
355107543Sscottl		fi
356107543Sscottl	fi
357107543Sscottl}
358107543Sscottl
359107543Sscottl# get_homedir
360107543Sscottl#	Reads the account's home directory. Used both with interactive input
361107543Sscottl#	and batch input.
362107543Sscottl#
363107543Sscottlget_homedir() {
364107543Sscottl	_input=
365107543Sscottl	if [ -z "$fflag" ]; then
366107543Sscottl		echo -n "Home directory [${homeprefix}/${username}]: "
367107543Sscottl		read _input
368107543Sscottl	else
369107543Sscottl		_input="`echo "$fileline" | cut -f8 -d:`"
370107543Sscottl	fi
371107543Sscottl
372107543Sscottl	if [ -n "$_input" ]; then
373107543Sscottl		uhome="$_input"
374107543Sscottl		# if this is a configuration run, then user input is the home
375107543Sscottl		# directory prefix. Otherwise it is understood to
376107543Sscottl		# be $prefix/$user
377107543Sscottl		#
378107543Sscottl		[ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome"
379107543Sscottl	else
380107543Sscottl		uhome="${homeprefix}/${username}"
381107543Sscottl	fi
382130160Smtm}
383107543Sscottl
384107543Sscottl# get_uid
385107543Sscottl#	Reads a numeric userid in an interactive or batch session. Automatically
386107543Sscottl#	allocates one if it is not specified.
387107543Sscottl#
388107543Sscottlget_uid() {
389107543Sscottl	uuid=${uidstart}
390107543Sscottl	_input=
391127076Smtm	_prompt=
392127076Smtm
393127076Smtm	# No need to take down uids for a configuration saving run.
394127076Smtm	[ -n "$configflag" ] && return
395127076Smtm
396116627Smtm	if [ -n "$uuid" ]; then
397107543Sscottl		_prompt="Uid [$uuid]: "
398107543Sscottl	else
399107543Sscottl		_prompt="Uid (Leave empty for default): "
400107543Sscottl	fi
401107543Sscottl	if [ -z "$fflag" ]; then
402107543Sscottl		echo -n "$_prompt"
403107543Sscottl		read _input
404107543Sscottl	else
405127076Smtm		_input="`echo "$fileline" | cut -f2 -d:`"
406127076Smtm	fi
407107543Sscottl
408127076Smtm	[ -n "$_input" ] && uuid=$_input
409127076Smtm	uuid=`get_nextuid $uuid`
410127076Smtm	uidstart=$uuid
411127076Smtm}
412127076Smtm
413127076Smtm# get_class
414127076Smtm#	Reads login class of account. Can be used in interactive or batch mode.
415127076Smtm#
416107543Sscottlget_class() {
417107543Sscottl	uclass="$defaultclass"
418107543Sscottl	_input=
419107543Sscottl	_class=${uclass:-"default"}
420107543Sscottl
421107543Sscottl	if [ -z "$fflag" ]; then
422107543Sscottl		echo -n "Login class [$_class]: "
423107543Sscottl		read _input
424107543Sscottl	else
425107543Sscottl		_input="`echo "$fileline" | cut -f4 -d:`"
426107543Sscottl	fi
427107543Sscottl
428107543Sscottl	[ -n "$_input" ] && uclass="$_input"
429107543Sscottl}
430107543Sscottl
431107543Sscottl# get_logingroup
432107543Sscottl#	Reads user's login group. Can be used in both interactive and batch
433107543Sscottl#	modes. The specified value can be a group name or its numeric id.
434107543Sscottl#	This routine leaves the field blank if nothing is provided and
435107543Sscottl#	a default login group has not been set. The pw(8) command
436107543Sscottl#	will then provide a login group with the same name as the username.
437107543Sscottl#
438107543Sscottlget_logingroup() {
439107543Sscottl	ulogingroup="$defaultLgroup"
440107543Sscottl	_input=
441107543Sscottl
442107543Sscottl	if [ -z "$fflag" ]; then
443107543Sscottl		echo -n "Login group [${ulogingroup:-$username}]: "
444107543Sscottl		read _input
445107543Sscottl	else
446107543Sscottl		_input="`echo "$fileline" | cut -f3 -d:`"
447107543Sscottl	fi
448107543Sscottl
449107543Sscottl	# Pw(8) will use the username as login group if it's left empty
450107543Sscottl	[ -n "$_input" ] && ulogingroup="$_input"
451107543Sscottl}
452107543Sscottl
453107543Sscottl# get_groups
454107543Sscottl#	Read additional groups for the user. It can be used in both interactive
455107543Sscottl#	and batch modes.
456107543Sscottl#
457107543Sscottlget_groups() {
458107543Sscottl	ugroups="$defaultgroups"
459107543Sscottl	_input=
460107543Sscottl	_group=${ulogingroup:-"${username}"}
461107543Sscottl
462107543Sscottl	if [ -z "$configflag" ]; then
463109573Sfjoe		[ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username"
464107543Sscottl		[ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: "
465107543Sscottl	else
466107543Sscottl		[ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: "
467107543Sscottl	fi
468107543Sscottl	read _input
469107543Sscottl
470107543Sscottl	[ -n "$_input" ] && ugroups="$_input"
471107543Sscottl}
472107543Sscottl
473107543Sscottl# get_expire_dates
474107543Sscottl#	Read expiry information for the account and also for the password. This
475107543Sscottl#	routine is used only from batch processing mode.
476107543Sscottl#
477107543Sscottlget_expire_dates() {
478107543Sscottl	upwexpire="`echo "$fileline" | cut -f5 -d:`"
479107543Sscottl	uexpire="`echo "$fileline" | cut -f6 -d:`"
480107543Sscottl}
481107543Sscottl
482107543Sscottl# get_password
483107543Sscottl#	Read the password in batch processing mode. The password field matters
484107543Sscottl#	only when the password type is "yes" or "random". If the field is empty and the
485107543Sscottl#	password type is "yes", then it assumes the account has an empty passsword
486107543Sscottl#	and changes the password type accordingly. If the password type is "random"
487107543Sscottl#	and the password field is NOT empty, then it assumes the account will NOT
488107543Sscottl#	have a random password and set passwdtype to "yes."
489107543Sscottl#
490107543Sscottlget_password() {
491107543Sscottl	# We may temporarily change a password type. Make sure it's changed
492107543Sscottl	# back to whatever it was before we process the next account.
493107543Sscottl	#
494107543Sscottl	[ -n "$savedpwtype" ] && {
495112433Smtm		passwdtype=$savedpwtype
496112433Smtm		savedpwtype=
497112433Smtm	}
498107543Sscottl
499107543Sscottl	# There may be a ':' in the password
500112433Smtm	upass=${fileline#*:*:*:*:*:*:*:*:*:}
501107543Sscottl
502107543Sscottl	if [ -z "$upass" ]; then
503107543Sscottl		case $passwdtype in
504112433Smtm		yes)
505107543Sscottl			# if it's empty, assume an empty password
506107543Sscottl			passwdtype=none
507107543Sscottl			savedpwtype=yes
508107543Sscottl			;;
509107543Sscottl		esac
510107543Sscottl	else
511112433Smtm		case $passwdtype in
512107543Sscottl		random)
513107543Sscottl			passwdtype=yes
514107543Sscottl			savedpwtype=random
515107543Sscottl			;;
516107543Sscottl		esac
517107543Sscottl	fi
518107543Sscottl}
519107543Sscottl
520107543Sscottl# input_from_file
521107543Sscottl#	Reads a line of account information from standard input and
522107543Sscottl#	adds it to the user database.
523107543Sscottl#
524107543Sscottlinput_from_file() {
525107543Sscottl	_field=
526107543Sscottl
527107543Sscottl	while read -r fileline ; do
528107543Sscottl		case "$fileline" in
529107543Sscottl		\#*|'')
530107543Sscottl			return 0
531107543Sscottl			;;
532107543Sscottl		esac
533107543Sscottl
534107543Sscottl		get_user || continue
535107543Sscottl		get_gecos
536107543Sscottl		get_uid
537107543Sscottl		get_logingroup
538107543Sscottl		get_class
539107543Sscottl		get_shell
540107543Sscottl		get_homedir
541107543Sscottl		get_password
542107543Sscottl		get_expire_dates
543107543Sscottl
544107543Sscottl		add_user
545107543Sscottl	done
546107543Sscottl}
547107543Sscottl
548107543Sscottl# input_interactive
549107543Sscottl#	Prompts for user information interactively, and commits to
550107543Sscottl#	the user database.
551107543Sscottl#
552107543Sscottlinput_interactive() {
553107543Sscottl
554107543Sscottl	_disable=
555107543Sscottl	_pass=
556107543Sscottl	_passconfirm=
557107543Sscottl	_random="no"
558107543Sscottl	_emptypass="no"
559107543Sscottl	_usepass="yes"
560107543Sscottl	_logingroup_ok="no"
561107543Sscottl	_groups_ok="no"
562107543Sscottl	case $passwdtype in
563107543Sscottl	none)
564107543Sscottl		_emptypass="yes"
565107543Sscottl		_usepass="yes"
566107543Sscottl		;;
567107543Sscottl	no)
568107543Sscottl		_usepass="no"
569107543Sscottl		;;
570107543Sscottl	random)
571107543Sscottl		_random="yes"
572107543Sscottl		;;
573107543Sscottl	esac
574107543Sscottl
575107543Sscottl	get_user
576107543Sscottl	get_gecos
577107543Sscottl	get_uid
578107543Sscottl
579107543Sscottl	# The case where group = user is handled elsewhere, so
580107543Sscottl	# validate any other groups the user is invited to.
581107543Sscottl	until [ "$_logingroup_ok" = yes ]; do
582107543Sscottl		get_logingroup
583107543Sscottl		_logingroup_ok=yes
584107543Sscottl		if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then
585107543Sscottl			if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then
586107543Sscottl				echo "Group $ulogingroup does not exist!"
587107543Sscottl				_logingroup_ok=no
588110595Smtm			fi
589107543Sscottl		fi
590107543Sscottl	done
591107543Sscottl	until [ "$_groups_ok" = yes ]; do
592107543Sscottl		get_groups
593107543Sscottl		_groups_ok=yes
594107543Sscottl		for i in $ugroups; do
595107543Sscottl			if [ "$username" != "$i" ]; then
596107543Sscottl				if ! ${PWCMD} show group $i > /dev/null 2>&1; then
597107543Sscottl					echo "Group $i does not exist!"
598107543Sscottl					_groups_ok=no
599107543Sscottl				fi
600107543Sscottl			fi
601107543Sscottl		done
602107543Sscottl	done
603107543Sscottl
604107543Sscottl	get_class
605107543Sscottl	get_shell
606107543Sscottl	get_homedir
607107543Sscottl
608107543Sscottl	while : ; do
609107543Sscottl		echo -n "Use password-based authentication? [$_usepass]: "
610107543Sscottl		read _input
611107543Sscottl		[ -z "$_input" ] && _input=$_usepass
612107543Sscottl		case $_input in
613107543Sscottl		[Nn][Oo]|[Nn])
614107543Sscottl			passwdtype="no"
615107543Sscottl			;;
616107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
617107543Sscottl			while : ; do
618107543Sscottl				echo -n "Use an empty password? (yes/no) [$_emptypass]: "
619107543Sscottl				read _input
620107543Sscottl				[ -n "$_input" ] && _emptypass=$_input
621112401Smtm				case $_emptypass in
622112401Smtm				[Nn][Oo]|[Nn])
623107543Sscottl					echo -n "Use a random password? (yes/no) [$_random]: "
624107543Sscottl					read _input
625107543Sscottl					[ -n "$_input" ] && _random="$_input"
626107543Sscottl					case $_random in
627107543Sscottl					[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
628107543Sscottl						passwdtype="random"
629107543Sscottl						break
630107543Sscottl						;;
631107543Sscottl					esac
632107543Sscottl					passwdtype="yes"
633107543Sscottl					[ -n "$configflag" ] && break
634107543Sscottl					trap 'stty echo; exit' 0 1 2 3 15
635107543Sscottl					stty -echo
636107543Sscottl					echo -n "Enter password: "
637107543Sscottl					read -r upass
638107543Sscottl					echo''
639110537Sadrian					echo -n "Enter password again: "
640110537Sadrian					read _passconfirm
641110537Sadrian					echo ''
642110537Sadrian					stty echo
643110537Sadrian					# if user entered a blank password
644110537Sadrian					# explicitly ask again.
645110537Sadrian					[ -z "$upass" -a -z "$_passconfirm" ] \
646110537Sadrian					    && continue
647110537Sadrian					;;
648110537Sadrian				[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
649110537Sadrian					passwdtype="none"
650110537Sadrian					break;
651110537Sadrian					;;
652110537Sadrian				*)
653110537Sadrian					# invalid answer; repeat the loop
654110537Sadrian					continue
655110537Sadrian					;;
656110537Sadrian				esac
657110537Sadrian				if [ "$upass" != "$_passconfirm" ]; then
658110537Sadrian					echo "Passwords did not match!"
659110537Sadrian					continue
660110537Sadrian				fi
661110537Sadrian				break
662110537Sadrian			done
663110537Sadrian			;;
664110537Sadrian		*)
665107543Sscottl			# invalid answer; repeat loop
666107543Sscottl			continue
667107543Sscottl			;;
668107543Sscottl		esac
669107543Sscottl		break;
670107543Sscottl	done
671107543Sscottl	_disable=${disableflag:-"no"}
672107543Sscottl	while : ; do
673107543Sscottl		echo -n "Lock out the account after creation? [$_disable]: "
674107543Sscottl		read _input
675107543Sscottl		[ -z "$_input" ] && _input=$_disable
676107543Sscottl		case $_input in
677107543Sscottl		[Nn][Oo]|[Nn])
678107543Sscottl			disableflag=
679107543Sscottl			;;
680107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
681107543Sscottl			disableflag=yes
682107543Sscottl			;;
683107543Sscottl		*)
684107543Sscottl			# invalid answer; repeat loop
685107543Sscottl			continue
686107543Sscottl			;;
687107543Sscottl		esac
688107543Sscottl		break
689107543Sscottl	done
690107543Sscottl	
691107543Sscottl	# Display the information we have so far and prompt to
692107543Sscottl	# commit it.
693107543Sscottl	#
694112401Smtm	_disable=${disableflag:-"no"}
695107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" Username $username
696107543Sscottl	case $passwdtype in
697107543Sscottl	yes)
698110595Smtm		_pass='*****'
699107543Sscottl		;;
700107543Sscottl	no)
701116623Smtm		_pass='<disabled>'
702107543Sscottl		;;
703107543Sscottl	none)
704107543Sscottl		_pass='<blank>'
705107543Sscottl		;;
706107543Sscottl	random)
707107543Sscottl		_pass='<random>'
708107543Sscottl		;;
709107543Sscottl	esac
710107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass"
711107543Sscottl	[ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype"
712107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos"
713107543Sscottl	[ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid"
714107543Sscottl	printf "%-10s : %s\n" "Class" "$uclass"
715107543Sscottl	printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups"
716107543Sscottl	printf "%-10s : %s\n" "Home" "$uhome"
717107543Sscottl	printf "%-10s : %s\n" "Shell" "$ushell"
718107543Sscottl	printf "%-10s : %s\n" "Locked" "$_disable"
719107543Sscottl	while : ; do
720107543Sscottl		echo -n "OK? (yes/no): "
721107543Sscottl		read _input
722107543Sscottl		case $_input in
723107543Sscottl		[Nn][Oo]|[Nn])
724107543Sscottl			return 1
725107543Sscottl			;;
726107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
727107543Sscottl			add_user
728107543Sscottl			;;
729107543Sscottl		*)
730107543Sscottl			continue
731107543Sscottl			;;
732107543Sscottl		esac
733107543Sscottl		break
734107543Sscottl	done
735107543Sscottl	return 0
736107543Sscottl}
737107543Sscottl
738107543Sscottl#### END SUBROUTINE DEFENITION ####
739107543Sscottl
740107543SscottlTHISCMD=`/usr/bin/basename $0`
741107543SscottlDEFAULTSHELL=/bin/sh
742107543SscottlADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
743107543SscottlPWCMD="${PWCMD:-/usr/sbin/pw}"
744107543SscottlMAILCMD="${MAILCMD:-mail}"
745107543SscottlETCSHELLS="${ETCSHELLS:-/etc/shells}"
746107543SscottlGREPCMD="/usr/bin/grep"
747107543SscottlDATECMD="/bin/date"
748107543Sscottl
749107543Sscottl# Set default values
750107543Sscottl#
751107543Sscottlusername=
752107543Sscottluuid=
753107543Sscottluidstart=
754107543Sscottlugecos=
755107543Sscottlulogingroup=
756107543Sscottluclass=
757107543Sscottluhome=
758107543Sscottlupass=
759107543Sscottlushell=
760107543Sscottludotdir=/usr/share/skel
761107543Sscottlugroups=
762107543Sscottluexpire=
763107543Sscottlupwexpire=
764107543Sscottlshells="`valid_shells`"
765107543Sscottlpasswdtype="yes"
766107543Sscottlmsgfile=/etc/adduser.msg
767107543Sscottlmsgflag=
768107543Sscottlquietflag=
769107543Sscottlconfigflag=
770107543Sscottlfflag=
771109751Sfjoeinfile=
772109751Sfjoedisableflag=
773107543Sscottlreadconfig="yes"
774107543Sscottlhomeprefix="/home"
775107543Sscottlrandompass=
776112433Smtmfileline=
777107543Sscottlsavedpwtype=
778107543Sscottldefaultclass=
779107543SscottldefaultLgroup=
780107543Sscottldefaultgoups=
781107543Sscottldefaultshell="${DEFAULTSHELL}"
782107543Sscottl
783107543Sscottl# Make sure the user running this program is root. This isn't a security
784107543Sscottl# measure as much as it is a usefull method of reminding the user to
785107543Sscottl# 'su -' before he/she wastes time entering data that won't be saved.
786107543Sscottl#
787107543Sscottlprocowner=${procowner:-`/usr/bin/id -u`}
788107543Sscottlif [ "$procowner" != "0" ]; then
789107543Sscottl	err 'you must be the super-user (uid 0) to use this utility.'
790107543Sscottl	exit 1
791107543Sscottlfi
792107543Sscottl
793107543Sscottl# Overide from our conf file
794107543Sscottl# Quickly go through the commandline line to see if we should read
795107543Sscottl# from our configuration file. The actual parsing of the commandline
796107543Sscottl# arguments happens after we read in our configuration file (commandline
797107543Sscottl# should override configuration file).
798107543Sscottl#
799107543Sscottlfor _i in $* ; do
800107543Sscottl	if [ "$_i" = "-N" ]; then
801107543Sscottl		readconfig=
802107543Sscottl		break;
803107543Sscottl	fi
804107543Sscottldone
805107543Sscottlif [ -n "$readconfig" ]; then
806107543Sscottl	# On a long-lived system, the first time this script is run it
807127076Smtm	# will barf upon reading the configuration file for its perl predecessor.
808116627Smtm	if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then
809127635Scperciva		[ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1
810109751Sfjoe	fi
811109751Sfjoefi 
812107543Sscottl
813107543Sscottl# Proccess command-line options
814107543Sscottl#
815107543Sscottlfor _switch ; do
816107543Sscottl	case $_switch in
817107543Sscottl	-L)
818107543Sscottl		defaultclass="$2"
819107543Sscottl		shift; shift
820107543Sscottl		;;
821107543Sscottl	-C)
822107543Sscottl		configflag=yes
823107543Sscottl		shift
824107543Sscottl		;;
825107543Sscottl	-E)
826107543Sscottl		disableflag=yes
827107543Sscottl		shift
828107543Sscottl		;;
829107543Sscottl	-k)
830107543Sscottl		udotdir="$2"
831107543Sscottl		shift; shift
832107543Sscottl		;;
833107543Sscottl	-f)
834107543Sscottl		[ "$2" != "-" ] && infile="$2"
835107543Sscottl		fflag=yes
836107543Sscottl		shift; shift
837127076Smtm		;;
838127076Smtm	-g)
839107543Sscottl		defaultLgroup="$2"
840107543Sscottl		shift; shift
841107543Sscottl		;;
842107543Sscottl	-G)
843107543Sscottl		defaultgroups="$2"
844107543Sscottl		shift; shift
845112433Smtm		;;
846116784Smtm	-h)
847107543Sscottl		show_usage
848107543Sscottl		exit 0
849107543Sscottl		;;
850107543Sscottl	-d)
851107543Sscottl		homeprefix="$2"
852107543Sscottl		shift; shift
853107543Sscottl		;;
854107543Sscottl	-m)
855107543Sscottl		case "$2" in
856107543Sscottl		[Nn][Oo])
857107543Sscottl			msgflag=
858107543Sscottl			;;
859107543Sscottl		*)
860107543Sscottl			msgflag=yes
861107543Sscottl			msgfile="$2"
862107543Sscottl			;;
863107543Sscottl		esac
864107543Sscottl		shift; shift
865107543Sscottl		;;
866107543Sscottl	-N)
867107543Sscottl		readconfig=
868107543Sscottl		shift
869107543Sscottl		;;
870107543Sscottl	-w)
871107543Sscottl		case "$2" in
872107543Sscottl		no|none|random|yes)
873107543Sscottl			passwdtype=$2
874107543Sscottl			;;
875107543Sscottl		*)
876107543Sscottl			show_usage
877107543Sscottl			exit 1
878107543Sscottl			;;
879107543Sscottl		esac
880107543Sscottl		shift; shift
881107543Sscottl		;;
882107543Sscottl	-q)
883107543Sscottl		quietflag=yes
884107543Sscottl		shift
885107543Sscottl		;;
886107543Sscottl	-s)
887107543Sscottl		defaultshell="`fullpath_from_shell $2`"
888107543Sscottl		shift; shift
889107543Sscottl		;;
890107543Sscottl	-u)
891127076Smtm		uidstart=$2
892127076Smtm		shift; shift
893127076Smtm		;;
894127076Smtm	esac
895107543Sscottldone
896107543Sscottl
897107543Sscottl# If the -f switch was used, get input from a file. Otherwise,
898107543Sscottl# this is an interactive session.
899107543Sscottl#
900107543Sscottlif [ -n "$fflag" ]; then
901107543Sscottl	if [ -z "$infile" ]; then
902107543Sscottl		input_from_file
903107543Sscottl	elif [ -n "$infile" ]; then
904107543Sscottl		if [ -r "$infile" ]; then
905107543Sscottl			input_from_file < $infile
906107543Sscottl		else
907107543Sscottl			err "File ($infile) is unreadable or does not exist."
908112433Smtm		fi
909112433Smtm	fi
910112433Smtmelse
911112433Smtm	input_interactive
912107543Sscottl	while : ; do
913107543Sscottl		if [ -z "$configflag" ]; then
914107543Sscottl			echo -n "Add another user? (yes/no): "
915107543Sscottl		else
916107543Sscottl			echo -n "Re-edit the default configuration? (yes/no): "
917107543Sscottl		fi
918107543Sscottl		read _input
919107543Sscottl		case $_input in
920107543Sscottl		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
921107543Sscottl			uidstart=`get_nextuid $uidstart`
922107543Sscottl			input_interactive
923107543Sscottl			continue
924107543Sscottl			;;
925107543Sscottl		[Nn][Oo]|[Nn])
926107543Sscottl			echo "Goodbye!"
927107543Sscottl			;;
928107543Sscottl		*)
929107543Sscottl			continue
930107543Sscottl			;;
931107543Sscottl		esac
932107543Sscottl		break
933107543Sscottl	done
934107543Sscottlfi
935107543Sscottl