adduser.sh revision 135616
112891Swpaul#!/bin/sh
212891Swpaul#
312891Swpaul# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved.
412891Swpaul#
512891Swpaul# Redistribution and use in source and binary forms, with or without
612891Swpaul# modification, are permitted provided that the following conditions
712891Swpaul# are met:
812891Swpaul# 1. Redistributions of source code must retain the above copyright
912891Swpaul#    notice, this list of conditions and the following disclaimer.
1012891Swpaul# 2. Redistributions in binary form must reproduce the above copyright
1112891Swpaul#    notice, this list of conditions and the following disclaimer in the
1212891Swpaul#    documentation and/or other materials provided with the distribution.
1312891Swpaul#
1412891Swpaul# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1512891Swpaul# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1612891Swpaul# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1712891Swpaul# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1812891Swpaul# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1912891Swpaul# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2012891Swpaul# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2112891Swpaul# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2212891Swpaul# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2312891Swpaul# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2412891Swpaul#
2512891Swpaul#       Email: Mike Makonnen <mtm@FreeBSD.Org>
2612891Swpaul#
2712891Swpaul# $FreeBSD: head/usr.sbin/adduser/adduser.sh 135616 2004-09-23 13:09:42Z roam $
2812891Swpaul#
2912891Swpaul
3012891Swpaul# err msg
3112891Swpaul#       Display $msg on stderr, unless we're being quiet.
3212891Swpaul# 
3312891Swpaulerr() {
34114601Sobrien	if [ -z "$quietflag" ]; then
35114601Sobrien        	echo 1>&2 ${THISCMD}: ERROR: $*
3630827Scharnier	fi
3720818Swpaul}
3812891Swpaul
3930827Scharnier# info msg
4030827Scharnier#       Display $msg on stdout, unless we're being quiet.
4112891Swpaul# 
4212891Swpaulinfo() {
4312891Swpaul	if [ -z "$quietflag" ]; then
4412891Swpaul        	echo ${THISCMD}: INFO: $*
4512891Swpaul	fi
4612891Swpaul}
4712891Swpaul
4812997Swpaul# get_nextuid
4912891Swpaul#	Output the value of $_uid if it is available for use. If it
5012891Swpaul#	is not, output the value of the next higher uid that is available.
5112891Swpaul#	If a uid is not specified, output the first available uid, as indicated
5228042Swpaul#	by pw(8).
5328042Swpaul# 
5428042Swpaulget_nextuid () {
5528042Swpaul	_uid=$1
5628042Swpaul	_nextuid=
5790298Sdes
5890298Sdes	if [ -z "$_uid" ]; then
5946186Swpaul		_nextuid="`${PWCMD} usernext | cut -f1 -d:`"
6046186Swpaul	else
6146186Swpaul		while : ; do
6246186Swpaul			${PWCMD} usershow $_uid > /dev/null 2>&1
6346186Swpaul			if [ ! "$?" -eq 0 ]; then
6446186Swpaul				_nextuid=$_uid
6546186Swpaul				break
6646186Swpaul			fi
6746186Swpaul			_uid=$(($_uid + 1))
6846186Swpaul		done
6914262Swpaul	fi
7014262Swpaul	echo $_nextuid
7114262Swpaul}
7214262Swpaul
7312891Swpaul# show_usage
7412891Swpaul#	Display usage information for this utility.
7512891Swpaul#
7612891Swpaulshow_usage() {
7712891Swpaul	echo "usage: ${THISCMD} [options]"
7812891Swpaul	echo "  options may include:"
7919161Swpaul	echo "  -C		save to the configuration file only"
8019161Swpaul	echo "  -D		do not attempt to create the home directory"
8119161Swpaul	echo "  -E		disable this account after creation"
8212891Swpaul	echo "  -G		additional groups to add accounts to"
8319161Swpaul	echo "  -L		login class of the user"
8412891Swpaul	echo "  -N		do not read configuration file"
8512891Swpaul	echo "  -S		a nonexistent shell is not an error"
8612891Swpaul	echo "  -d		home directory"
8712891Swpaul	echo "  -f		file from which input will be received"
8812891Swpaul	echo "  -g		default login group"
8912891Swpaul	echo "  -h		display this usage message"
9012891Swpaul	echo "  -k		path to skeleton home directory"
9112891Swpaul	echo "  -m		user welcome message file"
9212891Swpaul	echo "  -q		absolute minimal user feedback"
9312891Swpaul	echo "  -s		shell"
9412891Swpaul	echo "  -u		uid to start at"
9512891Swpaul	echo "  -w		password type: no, none, yes or random"
9619161Swpaul}
9719161Swpaul
9819161Swpaul# valid_shells
9912891Swpaul#	Outputs a list of valid shells from /etc/shells. Only the
10019161Swpaul#	basename of the shell is output.
10112891Swpaul#
10212891Swpaulvalid_shells() {
10312891Swpaul	_prefix=
10412891Swpaul	cat ${ETCSHELLS} |
10512891Swpaul	while read _path _junk ; do
10612891Swpaul		case $_path in
10712891Swpaul		\#*|'')
10812891Swpaul			;;
10912891Swpaul		*)
11012891Swpaul			echo -n "${_prefix}`basename $_path`"
11112891Swpaul			_prefix=' '
11212891Swpaul			;;
11312891Swpaul		esac
11412891Swpaul	done
11512891Swpaul
11612891Swpaul	# /usr/sbin/nologin is a special case
11712891Swpaul	[ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}"
11819161Swpaul}
11919161Swpaul
12019161Swpaul# fullpath_from_shell shell
12112891Swpaul#	Given $shell, which is either the full path to a shell or
12219161Swpaul#	the basename component of a valid shell, get the
12312891Swpaul#	full path to the shell from the /etc/shells file.
12412891Swpaul#
12512891Swpaulfullpath_from_shell() {
12612891Swpaul	_shell=$1
12712891Swpaul	[ -z "$_shell" ] && return 1
12812891Swpaul
12912891Swpaul	# /usr/sbin/nologin is a special case; it needs to be handled
13012891Swpaul	# before the cat | while loop, since a 'return' from within
13112891Swpaul	# a subshell will not terminate the function's execution, and
13212891Swpaul	# the path to the nologin shell might be printed out twice.
13312891Swpaul	#
13412891Swpaul	if [ "$_shell" = "${NOLOGIN}" -o \
13512891Swpaul	    "$_shell" = "${NOLOGIN_PATH}" ]; then
13612891Swpaul		echo ${NOLOGIN_PATH}
13712891Swpaul		return 0;
13814304Swpaul	fi
13914304Swpaul
14019161Swpaul	cat ${ETCSHELLS} |
14119161Swpaul	while read _path _junk ; do
14219161Swpaul		case "$_path" in
14319161Swpaul		\#*|'')
14412891Swpaul			;;
14519161Swpaul		*)
14612891Swpaul			if [ "$_path" = "$_shell" -o \
14712891Swpaul			    "`basename $_path`" = "$_shell" ]; then
14812891Swpaul				echo $_path
14912891Swpaul				return 0
15012891Swpaul			fi
15112891Swpaul			;;
15212891Swpaul		esac
15312891Swpaul	done
15412891Swpaul
15533250Swpaul	return 1
15620818Swpaul}
15720818Swpaul
15812891Swpaul# shell_exists shell
15912891Swpaul#	If the given shell is listed in ${ETCSHELLS} or it is
16020818Swpaul#	the nologin shell this function will return 0.
16120818Swpaul#	Otherwise, it will return 1. If shell is valid but
16212891Swpaul#	the path is invalid or it is not executable it
16312891Swpaul#	will emit an informational message saying so.
16412891Swpaul#
16512891Swpaulshell_exists()
16619161Swpaul{
167159461Smaxim	_sh="$1"
16819161Swpaul	_shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1"
169159461Smaxim
17019161Swpaul	if ! eval $_shellchk; then
171145792Sume		# The nologin shell is not listed in /etc/shells.
172145793Sume		if [ "$_sh" != "${NOLOGIN_PATH}" ]; then
17319161Swpaul			err "Invalid shell ($_sh) for user $username."
17427589Swpaul			return 1
17590297Sdes		fi
17620818Swpaul	fi
17727589Swpaul	! [ -x "$_sh" ] &&
17827589Swpaul	    info "The shell ($_sh) does not exist or is not executable."
17912891Swpaul
18012891Swpaul	return 0
18130827Scharnier}
18212891Swpaul
18312891Swpaul# save_config
184145792Sume#	Save some variables to a configuration file.
185145792Sume#	Note: not all script variables are saved, only those that
18612891Swpaul#	      it makes sense to save.
187145792Sume#
188145792Sumesave_config() {
189145792Sume	echo "# Configuration file for adduser(8)."     >  ${ADDUSERCONF}
190145792Sume	echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF}
191145792Sume	echo "# Last Modified on `${DATECMD}`."		>> ${ADDUSERCONF}
192145792Sume	echo ''				>> ${ADDUSERCONF}
193145792Sume	echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF}
194145792Sume	echo "defaultclass=$uclass"	>> ${ADDUSERCONF}
19512891Swpaul	echo "defaultgroups=$ugroups"	>> ${ADDUSERCONF}
19620818Swpaul	echo "passwdtype=$passwdtype" 	>> ${ADDUSERCONF}
19720818Swpaul	echo "homeprefix=$homeprefix" 	>> ${ADDUSERCONF}
19812891Swpaul	echo "defaultshell=$ushell"	>> ${ADDUSERCONF}
19912891Swpaul	echo "udotdir=$udotdir"		>> ${ADDUSERCONF}
20012891Swpaul	echo "msgfile=$msgfile"		>> ${ADDUSERCONF}
20112891Swpaul	echo "disableflag=$disableflag" >> ${ADDUSERCONF}
20212891Swpaul}
20312891Swpaul
20412891Swpaul# add_user
20512891Swpaul#	Add a user to the user database. If the user chose to send a welcome
20612891Swpaul#	message or lock the account, do so.
20712891Swpaul#
20814304Swpauladd_user() {
20914304Swpaul
21019161Swpaul	# Is this a configuration run? If so, don't modify user database.
21119161Swpaul	#
21219161Swpaul	if [ -n "$configflag" ]; then
21319161Swpaul		save_config
21412891Swpaul		return
21519161Swpaul	fi
21612891Swpaul
21712891Swpaul	_uid=
21812891Swpaul	_name=
21912891Swpaul	_comment=
22012891Swpaul	_gecos=
22112891Swpaul	_home=
22212891Swpaul	_group=
22312891Swpaul	_grouplist=
22412891Swpaul	_shell=
22533250Swpaul	_class=
22612891Swpaul	_dotdir=
22712891Swpaul	_expire=
22812891Swpaul	_pwexpire=
22912891Swpaul	_passwd=
23020818Swpaul	_upasswd=
23112891Swpaul	_passwdmethod=
23212891Swpaul
23312891Swpaul	_name="-n '$username'"
23412891Swpaul	[ -n "$uuid" ] && _uid='-u "$uuid"'
23512891Swpaul	[ -n "$ulogingroup" ] && _group='-g "$ulogingroup"'
23612891Swpaul	[ -n "$ugroups" ] && _grouplist='-G "$ugroups"'
23712891Swpaul	[ -n "$ushell" ] && _shell='-s "$ushell"'
23812891Swpaul	[ -n "$uclass" ] && _class='-L "$uclass"'
23912891Swpaul	[ -n "$ugecos" ] && _comment='-c "$ugecos"'
24014304Swpaul	[ -n "$udotdir" ] && _dotdir='-k "$udotdir"'
24114304Swpaul	[ -n "$uexpire" ] && _expire='-e "$uexpire"'
24215426Swpaul	[ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"'
24319161Swpaul	if [ -z "$Dflag" -a -n "$uhome" ]; then
24419161Swpaul		# The /nonexistent home directory is special. It
24519161Swpaul		# means the user has no home directory.
24612891Swpaul		if [ "$uhome" = "$NOHOME" ]; then
24719161Swpaul			_home='-d "$uhome"'
24812891Swpaul		else
24912891Swpaul			_home='-m -d "$uhome"'
25012891Swpaul		fi
25112891Swpaul	elif [ -n "$Dflag" -a -n "$uhome" ]; then
25212891Swpaul		_home='-d "$uhome"'
25312891Swpaul	fi
25412891Swpaul	case $passwdtype in
25512891Swpaul	no)
25612891Swpaul		_passwdmethod="-w no"
25720818Swpaul		_passwd="-h -"
25812891Swpaul		;;
25912891Swpaul	yes)
26012891Swpaul		# Note on processing the password: The outer double quotes
26112891Swpaul		# make literal everything except ` and \ and $.
26220818Swpaul		# The outer single quotes make literal ` and $.
26320818Swpaul		# We can ensure the \ isn't treated specially by specifying
26412891Swpaul		# the -r switch to the read command used to obtain the input.
26520818Swpaul		#
26620818Swpaul		_passwdmethod="-w yes"
26712891Swpaul		_passwd="-h 0"
26812891Swpaul		_upasswd='echo "$upass" |'
26912891Swpaul		;;
27090298Sdes	none)
27190298Sdes		_passwdmethod="-w none"
27290298Sdes		;;
27312997Swpaul	random)
27412997Swpaul		_passwdmethod="-w random"
27512997Swpaul		;;
27612997Swpaul	esac
27712997Swpaul
27813375Swpaul	_pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment"
27912997Swpaul	_pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"
28013375Swpaul	_pwcmd="$_pwcmd $_expire $_pwexpire"
28112997Swpaul
28212997Swpaul	if ! _output=`eval $_pwcmd` ; then
28312997Swpaul		err "There was an error adding user ($username)."
28419131Swpaul		return 1
28519131Swpaul	else
28619161Swpaul		info "Successfully added ($username) to the user database."
28719131Swpaul		if [ "random" = "$passwdtype" ]; then
28819131Swpaul			randompass="$_output"
28912997Swpaul			info "Password for ($username) is: $randompass"
29012997Swpaul		fi
29112997Swpaul	fi
29212997Swpaul
29313375Swpaul	if [ -n "$disableflag" ]; then
29413375Swpaul		if ${PWCMD} lock $username ; then
29595658Sdes			info "Account ($username) is locked."
29613375Swpaul		else
29712997Swpaul			info "Account ($username) could NOT be locked."
29813375Swpaul		fi
29913375Swpaul	fi
30013375Swpaul
30113375Swpaul	_line=
30213375Swpaul	_owner=
30313375Swpaul	_perms=
30413375Swpaul	if [ -n "$msgflag" ]; then
30513375Swpaul		[ -r "$msgfile" ] && {
30612997Swpaul			# We're evaluating the contents of an external file.
30712997Swpaul			# Let's not open ourselves up for attack. _perms will
30812997Swpaul			# be empty if it's writeable only by the owner. _owner
30912997Swpaul			# will *NOT* be empty if the file is owned by root.
31015426Swpaul			#
31115426Swpaul			_dir="`dirname $msgfile`"
31215426Swpaul			_file="`basename $msgfile`"
31395658Sdes			_perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune`
31415426Swpaul			_owner=`/usr/bin/find $_dir -name $_file -user 0 -prune`
31515426Swpaul			if [ -z "$_owner" -o -n "$_perms" ]; then
31615426Swpaul				err "The message file ($msgfile) may be writeable only by root."
31715426Swpaul				return 1
31812891Swpaul			fi
31912891Swpaul			cat "$msgfile" |
32012891Swpaul			while read _line ; do
32112891Swpaul				eval echo "$_line"
32212997Swpaul			done | ${MAILCMD} -s"Welcome" ${username}
32324780Swpaul			info "Sent welcome message to ($username)."
32424780Swpaul		}
32512891Swpaul	fi
32613375Swpaul}
32713375Swpaul
32813375Swpaul# get_user
32919161Swpaul#	Reads username of the account from standard input or from a global
33019161Swpaul#	variable containing an account line from a file. The username is
33119161Swpaul#	required. If this is an interactive session it will prompt in
33219161Swpaul#	a loop until a username is entered. If it is batch processing from
33312891Swpaul#	a file it will output an error message and return to the caller.
33419161Swpaul#
33524780Swpaulget_user() {
33612891Swpaul	_input=
33712891Swpaul
33824780Swpaul	# No need to take down user names if this is a configuration saving run.
33912891Swpaul	[ -n "$configflag" ] && return
34024780Swpaul
34112891Swpaul	while : ; do
34212891Swpaul		if [ -z "$fflag" ]; then
34312891Swpaul			echo -n "Username: "
34424780Swpaul			read _input
34512891Swpaul		else
34612891Swpaul			_input="`echo "$fileline" | cut -f1 -d:`"
34724780Swpaul		fi
34824780Swpaul
34924780Swpaul		# There *must* be a username. If this is an interactive
35024780Swpaul		# session give the user an opportunity to retry.
35124780Swpaul		#
35224780Swpaul		if [ -z "$_input" ]; then
35324780Swpaul			err "You must enter a username!"
35424780Swpaul			[ -z "$fflag" ] && continue
35524780Swpaul		fi
35624780Swpaul		break
35724780Swpaul	done
35824780Swpaul	username="$_input"
35924780Swpaul}
36024780Swpaul
36124780Swpaul# get_gecos
36224780Swpaul#	Reads extra information about the user. Can be used both in interactive
36324780Swpaul#	and batch (from file) mode.
36424780Swpaul#
36524780Swpaulget_gecos() {
36624780Swpaul	_input=
36724780Swpaul
36890297Sdes	# No need to take down additional user information for a configuration run.
36912891Swpaul	[ -n "$configflag" ] && return
37012891Swpaul
37112891Swpaul	if [ -z "$fflag" ]; then
37212891Swpaul		echo -n "Full name: "
37312891Swpaul		read _input
37480184Skris	else
37580184Skris		_input="`echo "$fileline" | cut -f7 -d:`"
37680184Skris	fi
37780184Skris	ugecos="$_input"
37822321Swpaul}
37912997Swpaul
38022321Swpaul# get_shell
38112997Swpaul#	Get the account's shell. Works in interactive and batch mode. It
38214304Swpaul#	accepts either the base name of the shell or the full path.
38314304Swpaul#	If an invalid shell is entered it will simply use the default shell.
38424780Swpaul#
38514304Swpaulget_shell() {
38614304Swpaul	_input=
38714304Swpaul	_fullpath=
38895658Sdes	ushell="$defaultshell"
38912997Swpaul
39014304Swpaul	# Make sure the current value of the shell is a valid one
39114304Swpaul	if [ -z "$Sflag" ]; then
39224780Swpaul		if ! shell_exists $ushell ; then
39314304Swpaul			info "Using default shell ${defaultshell}."
39414304Swpaul			ushell="$defaultshell"
39514304Swpaul		fi
39695658Sdes	fi
39712997Swpaul
39815426Swpaul	if [ -z "$fflag" ]; then
39924780Swpaul		echo -n "Shell ($shells) [`basename $ushell`]: "
40046205Swpaul		read _input
40146205Swpaul	else
40246205Swpaul		_input="`echo "$fileline" | cut -f9 -d:`"
40346205Swpaul	fi
40446205Swpaul	if [ -n "$_input" ]; then
40546205Swpaul		if [ -n "$Sflag" ]; then
40646205Swpaul			ushell="$_input"
40712997Swpaul		else
40812891Swpaul			_fullpath=`fullpath_from_shell $_input`
40912891Swpaul			if [ -n "$_fullpath" ]; then
41012891Swpaul				ushell="$_fullpath"
41124780Swpaul			else
41212891Swpaul				err "Invalid shell ($_input) for user $username."
41312891Swpaul				info "Using default shell ${defaultshell}."
41413375Swpaul				ushell="$defaultshell"
41512997Swpaul			fi
41612891Swpaul		fi
41712891Swpaul	fi
41813375Swpaul}
41913375Swpaul
42012891Swpaul# get_homedir
42115426Swpaul#	Reads the account's home directory. Used both with interactive input
42212891Swpaul#	and batch input.
42312891Swpaul#
42412891Swpaulget_homedir() {
42512891Swpaul	_input=
42612891Swpaul	if [ -z "$fflag" ]; then
42712891Swpaul		echo -n "Home directory [${homeprefix}/${username}]: "
42812891Swpaul		read _input
42919161Swpaul	else
43019161Swpaul		_input="`echo "$fileline" | cut -f8 -d:`"
43119161Swpaul	fi
43212891Swpaul
43319161Swpaul	if [ -n "$_input" ]; then
43412891Swpaul		uhome="$_input"
43515426Swpaul		# if this is a configuration run, then user input is the home
43615426Swpaul		# directory prefix. Otherwise it is understood to
43715426Swpaul		# be $prefix/$user
43815426Swpaul		#
43914240Swpaul		[ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome"
44014240Swpaul	else
44114240Swpaul		uhome="${homeprefix}/${username}"
44212891Swpaul	fi
44312891Swpaul}
44412891Swpaul
44512891Swpaul# get_uid
44612891Swpaul#	Reads a numeric userid in an interactive or batch session. Automatically
44712891Swpaul#	allocates one if it is not specified.
44812891Swpaul#
44912891Swpaulget_uid() {
45012891Swpaul	uuid=${uidstart}
45112891Swpaul	_input=
45212891Swpaul	_prompt=
45312891Swpaul
45412891Swpaul	# No need to take down uids for a configuration saving run.
45512891Swpaul	[ -n "$configflag" ] && return
45612891Swpaul
45712891Swpaul	if [ -n "$uuid" ]; then
45812891Swpaul		_prompt="Uid [$uuid]: "
45912891Swpaul	else
46020100Swpaul		_prompt="Uid (Leave empty for default): "
46120100Swpaul	fi
46220100Swpaul	if [ -z "$fflag" ]; then
46312891Swpaul		echo -n "$_prompt"
46412891Swpaul		read _input
46512891Swpaul	else
46612891Swpaul		_input="`echo "$fileline" | cut -f2 -d:`"
46720100Swpaul	fi
46820100Swpaul
46920100Swpaul	[ -n "$_input" ] && uuid=$_input
47020818Swpaul	uuid=`get_nextuid $uuid`
47120818Swpaul	uidstart=$uuid
47220100Swpaul}
47320100Swpaul
47420100Swpaul# get_class
47520100Swpaul#	Reads login class of account. Can be used in interactive or batch mode.
47620100Swpaul#
47720100Swpaulget_class() {
47820100Swpaul	uclass="$defaultclass"
47920100Swpaul	_input=
48020100Swpaul	_class=${uclass:-"default"}
48120100Swpaul
48220100Swpaul	if [ -z "$fflag" ]; then
48312891Swpaul		echo -n "Login class [$_class]: "
48412891Swpaul		read _input
48512891Swpaul	else
48612891Swpaul		_input="`echo "$fileline" | cut -f4 -d:`"
48712891Swpaul	fi
48812891Swpaul
48912891Swpaul	[ -n "$_input" ] && uclass="$_input"
49012891Swpaul}
49112891Swpaul
49212891Swpaul# get_logingroup
49312891Swpaul#	Reads user's login group. Can be used in both interactive and batch
49412891Swpaul#	modes. The specified value can be a group name or its numeric id.
49512891Swpaul#	This routine leaves the field blank if nothing is provided and
49614304Swpaul#	a default login group has not been set. The pw(8) command
49714304Swpaul#	will then provide a login group with the same name as the username.
49812891Swpaul#
49919161Swpaulget_logingroup() {
50019161Swpaul	ulogingroup="$defaultLgroup"
50119161Swpaul	_input=
50212891Swpaul
50319161Swpaul	if [ -z "$fflag" ]; then
50412891Swpaul		echo -n "Login group [${ulogingroup:-$username}]: "
50512891Swpaul		read _input
50612891Swpaul	else
50712891Swpaul		_input="`echo "$fileline" | cut -f3 -d:`"
50812891Swpaul	fi
50912891Swpaul
51012891Swpaul	# Pw(8) will use the username as login group if it's left empty
51112891Swpaul	[ -n "$_input" ] && ulogingroup="$_input"
51212891Swpaul}
51320100Swpaul
51421389Swpaul# get_groups
51521389Swpaul#	Read additional groups for the user. It can be used in both interactive
51621389Swpaul#	and batch modes.
51721389Swpaul#
51821389Swpaulget_groups() {
51921389Swpaul	ugroups="$defaultgroups"
52021389Swpaul	_input=
52121389Swpaul	_group=${ulogingroup:-"${username}"}
52221389Swpaul
52321389Swpaul	if [ -z "$configflag" ]; then
52420100Swpaul		[ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username"
52520100Swpaul		[ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: "
52620100Swpaul	else
52720100Swpaul		[ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: "
52820100Swpaul	fi
52943847Swpaul	read _input
53090297Sdes
53143847Swpaul	[ -n "$_input" ] && ugroups="$_input"
53243847Swpaul}
53343847Swpaul
53443847Swpaul# get_expire_dates
53543847Swpaul#	Read expiry information for the account and also for the password. This
53643847Swpaul#	routine is used only from batch processing mode.
53743847Swpaul#
53843847Swpaulget_expire_dates() {
53943847Swpaul	upwexpire="`echo "$fileline" | cut -f5 -d:`"
54043847Swpaul	uexpire="`echo "$fileline" | cut -f6 -d:`"
54143847Swpaul}
54243847Swpaul
54320100Swpaul# get_password
54420100Swpaul#	Read the password in batch processing mode. The password field matters
54546207Swpaul#	only when the password type is "yes" or "random". If the field is empty and the
54646207Swpaul#	password type is "yes", then it assumes the account has an empty passsword
54746207Swpaul#	and changes the password type accordingly. If the password type is "random"
54846207Swpaul#	and the password field is NOT empty, then it assumes the account will NOT
54946207Swpaul#	have a random password and set passwdtype to "yes."
55046207Swpaul#
55146207Swpaulget_password() {
55246207Swpaul	# We may temporarily change a password type. Make sure it's changed
55320818Swpaul	# back to whatever it was before we process the next account.
55420818Swpaul	#
55512891Swpaul	[ -n "$savedpwtype" ] && {
55612891Swpaul		passwdtype=$savedpwtype
55712891Swpaul		savedpwtype=
55812891Swpaul	}
55912891Swpaul
56095658Sdes	# There may be a ':' in the password
56120100Swpaul	upass=${fileline#*:*:*:*:*:*:*:*:*:}
56212891Swpaul
56346205Swpaul	if [ -z "$upass" ]; then
56446205Swpaul		case $passwdtype in
56512891Swpaul		yes)
566200100Skuriyama			# if it's empty, assume an empty password
567200100Skuriyama			passwdtype=none
568200100Skuriyama			savedpwtype=yes
569200100Skuriyama			;;
57012891Swpaul		esac
57112891Swpaul	else
57212891Swpaul		case $passwdtype in
57312891Swpaul		random)
57412891Swpaul			passwdtype=yes
57512891Swpaul			savedpwtype=random
57615426Swpaul			;;
57728042Swpaul		esac
57820818Swpaul	fi
57912891Swpaul}
58014303Swpaul
58114303Swpaul# input_from_file
58219161Swpaul#	Reads a line of account information from standard input and
58319161Swpaul#	adds it to the user database.
58419161Swpaul#
58519161Swpaulinput_from_file() {
58619161Swpaul	_field=
58712891Swpaul
58812891Swpaul	while read -r fileline ; do
58912891Swpaul		case "$fileline" in
59012891Swpaul		\#*|'')
59112891Swpaul			return 0
59212891Swpaul			;;
59312891Swpaul		esac
59412891Swpaul
59512891Swpaul		get_user || continue
59620818Swpaul		get_gecos
59720818Swpaul		get_uid
59820818Swpaul		get_logingroup
59920818Swpaul		get_class
60020818Swpaul		get_shell
60115426Swpaul		get_homedir
60215426Swpaul		get_password
60315426Swpaul		get_expire_dates
60415426Swpaul
60515426Swpaul		add_user
60615426Swpaul	done
60715426Swpaul}
60815426Swpaul
60920818Swpaul# input_interactive
61020818Swpaul#	Prompts for user information interactively, and commits to
61195658Sdes#	the user database.
61220818Swpaul#
61395658Sdesinput_interactive() {
61412891Swpaul
61512891Swpaul	_disable=
61612891Swpaul	_pass=
61712891Swpaul	_passconfirm=
61812891Swpaul	_random="no"
61912891Swpaul	_emptypass="no"
62012891Swpaul	_usepass="yes"
62112891Swpaul	_logingroup_ok="no"
62212891Swpaul	_groups_ok="no"
62312891Swpaul	case $passwdtype in
62428042Swpaul	none)
62520818Swpaul		_emptypass="yes"
62612891Swpaul		_usepass="yes"
62714304Swpaul		;;
62814304Swpaul	no)
62919161Swpaul		_usepass="no"
63019161Swpaul		;;
63119161Swpaul	random)
63219161Swpaul		_random="yes"
63319161Swpaul		;;
63412891Swpaul	esac
63512891Swpaul
63612891Swpaul	get_user
63712891Swpaul	get_gecos
63812891Swpaul	get_uid
63912891Swpaul
64012891Swpaul	# The case where group = user is handled elsewhere, so
64112891Swpaul	# validate any other groups the user is invited to.
64290297Sdes	until [ "$_logingroup_ok" = yes ]; do
64312891Swpaul		get_logingroup
64412891Swpaul		_logingroup_ok=yes
64512891Swpaul		if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then
64612891Swpaul			if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then
64712891Swpaul				echo "Group $ulogingroup does not exist!"
64812891Swpaul				_logingroup_ok=no
64912891Swpaul			fi
65020818Swpaul		fi
65120818Swpaul	done
65220818Swpaul	until [ "$_groups_ok" = yes ]; do
65320818Swpaul		get_groups
65420818Swpaul		_groups_ok=yes
65520818Swpaul		for i in $ugroups; do
65620818Swpaul			if [ "$username" != "$i" ]; then
65720818Swpaul				if ! ${PWCMD} show group $i > /dev/null 2>&1; then
65895658Sdes					echo "Group $i does not exist!"
65912891Swpaul					_groups_ok=no
66012891Swpaul				fi
66112891Swpaul			fi
66212891Swpaul		done
66312891Swpaul	done
66412891Swpaul
66590298Sdes	get_class
66612891Swpaul	get_shell
66712891Swpaul	get_homedir
66812891Swpaul
66990297Sdes	while : ; do
67012891Swpaul		echo -n "Use password-based authentication? [$_usepass]: "
67112891Swpaul		read _input
67212891Swpaul		[ -z "$_input" ] && _input=$_usepass
67312891Swpaul		case $_input in
67412891Swpaul		[Nn][Oo]|[Nn])
67512891Swpaul			passwdtype="no"
67612891Swpaul			;;
67712891Swpaul		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
67890298Sdes			while : ; do
67990298Sdes				echo -n "Use an empty password? (yes/no) [$_emptypass]: "
68012891Swpaul				read _input
68112891Swpaul				[ -n "$_input" ] && _emptypass=$_input
68212891Swpaul				case $_emptypass in
68312891Swpaul				[Nn][Oo]|[Nn])
68412891Swpaul					echo -n "Use a random password? (yes/no) [$_random]: "
68512891Swpaul					read _input
68612891Swpaul					[ -n "$_input" ] && _random="$_input"
68712891Swpaul					case $_random in
68812891Swpaul					[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
68912891Swpaul						passwdtype="random"
69012891Swpaul						break
69112891Swpaul						;;
69213800Swpaul					esac
69312891Swpaul					passwdtype="yes"
69412891Swpaul					[ -n "$configflag" ] && break
69512891Swpaul					trap 'stty echo; exit' 0 1 2 3 15
69612891Swpaul					stty -echo
69712891Swpaul					echo -n "Enter password: "
69814304Swpaul					read -r upass
69914304Swpaul					echo''
70014304Swpaul					echo -n "Enter password again: "
70114304Swpaul					read -r _passconfirm
70212891Swpaul					echo ''
70314304Swpaul					stty echo
70416044Swpaul					# if user entered a blank password
70530827Scharnier					# explicitly ask again.
70612891Swpaul					[ -z "$upass" -a -z "$_passconfirm" ] \
70712891Swpaul					    && continue
70812891Swpaul					;;
70912891Swpaul				[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
71095658Sdes					passwdtype="none"
71114304Swpaul					break;
71212891Swpaul					;;
71312891Swpaul				*)
714300635Struckman					# invalid answer; repeat the loop
71512891Swpaul					continue
71612891Swpaul					;;
71712891Swpaul				esac
71812891Swpaul				if [ "$upass" != "$_passconfirm" ]; then
71912891Swpaul					echo "Passwords did not match!"
72012891Swpaul					continue
72112891Swpaul				fi
72212891Swpaul				break
72312891Swpaul			done
72412891Swpaul			;;
72512891Swpaul		*)
72612891Swpaul			# invalid answer; repeat loop
72712891Swpaul			continue
72812891Swpaul			;;
72912891Swpaul		esac
73012891Swpaul		break;
73115426Swpaul	done
73212891Swpaul	_disable=${disableflag:-"no"}
73319161Swpaul	while : ; do
73419161Swpaul		echo -n "Lock out the account after creation? [$_disable]: "
73519161Swpaul		read _input
73612891Swpaul		[ -z "$_input" ] && _input=$_disable
73719161Swpaul		case $_input in
73812891Swpaul		[Nn][Oo]|[Nn])
73912891Swpaul			disableflag=
74012891Swpaul			;;
74112891Swpaul		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
74212891Swpaul			disableflag=yes
74312891Swpaul			;;
74412891Swpaul		*)
74512891Swpaul			# invalid answer; repeat loop
74690297Sdes			continue
74712891Swpaul			;;
74812891Swpaul		esac
74912891Swpaul		break
75012891Swpaul	done
75112891Swpaul	
75212891Swpaul	# Display the information we have so far and prompt to
75312891Swpaul	# commit it.
75412891Swpaul	#
75512891Swpaul	_disable=${disableflag:-"no"}
75612891Swpaul	[ -z "$configflag" ] && printf "%-10s : %s\n" Username $username
75712891Swpaul	case $passwdtype in
75812891Swpaul	yes)
75912891Swpaul		_pass='*****'
76012891Swpaul		;;
76112891Swpaul	no)
76212891Swpaul		_pass='<disabled>'
76312891Swpaul		;;
76412891Swpaul	none)
76512891Swpaul		_pass='<blank>'
76612891Swpaul		;;
76712891Swpaul	random)
76812891Swpaul		_pass='<random>'
76912891Swpaul		;;
77012891Swpaul	esac
77112891Swpaul	[ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass"
77212891Swpaul	[ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype"
77314262Swpaul	[ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos"
77414262Swpaul	[ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid"
77514262Swpaul	printf "%-10s : %s\n" "Class" "$uclass"
77614262Swpaul	printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups"
77714262Swpaul	printf "%-10s : %s\n" "Home" "$uhome"
77814262Swpaul	printf "%-10s : %s\n" "Shell" "$ushell"
77914262Swpaul	printf "%-10s : %s\n" "Locked" "$_disable"
78014262Swpaul	while : ; do
78114262Swpaul		echo -n "OK? (yes/no): "
78214262Swpaul		read _input
78314262Swpaul		case $_input in
78414262Swpaul		[Nn][Oo]|[Nn])
78514262Swpaul			return 1
78614262Swpaul			;;
78714262Swpaul		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
78814262Swpaul			add_user
78914262Swpaul			;;
79014262Swpaul		*)
79114262Swpaul			continue
79214262Swpaul			;;
79314262Swpaul		esac
79414262Swpaul		break
79514262Swpaul	done
79614262Swpaul	return 0
79714262Swpaul}
79814262Swpaul
79914262Swpaul#### END SUBROUTINE DEFENITION ####
80014262Swpaul
80114262SwpaulTHISCMD=`/usr/bin/basename $0`
80214262SwpaulDEFAULTSHELL=/bin/sh
80314262SwpaulADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
80414262SwpaulPWCMD="${PWCMD:-/usr/sbin/pw}"
80514262SwpaulMAILCMD="${MAILCMD:-mail}"
80614262SwpaulETCSHELLS="${ETCSHELLS:-/etc/shells}"
80714262SwpaulNOHOME="/nonexistent"
80814262SwpaulNOLOGIN="nologin"
80914304SwpaulNOLOGIN_PATH="/usr/sbin/nologin"
81014304SwpaulGREPCMD="/usr/bin/grep"
81114304SwpaulDATECMD="/bin/date"
81214262Swpaul
81314262Swpaul# Set default values
81414262Swpaul#
81514262Swpaulusername=
81614262Swpauluuid=
81714262Swpauluidstart=
81814262Swpaulugecos=
81914304Swpaululogingroup=
82014304Swpauluclass=
82114262Swpauluhome=
82214262Swpaulupass=
82314262Swpaulushell=
82414262Swpauludotdir=/usr/share/skel
82514262Swpaulugroups=
82614262Swpauluexpire=
82714262Swpaulupwexpire=
82814262Swpaulshells="`valid_shells`"
82914262Swpaulpasswdtype="yes"
83014262Swpaulmsgfile=/etc/adduser.msg
83195658Sdesmsgflag=
83214262Swpaulquietflag=
83314262Swpaulconfigflag=
83414262Swpaulfflag=
83514262Swpaulinfile=
83614262Swpauldisableflag=
83714304SwpaulDflag=
83814304SwpaulSflag=
83914304Swpaulreadconfig="yes"
84014262Swpaulhomeprefix="/home"
84114262Swpaulrandompass=
84214262Swpaulfileline=
84314262Swpaulsavedpwtype=
84414262Swpauldefaultclass=
84514262SwpauldefaultLgroup=
84614262Swpauldefaultgroups=
84714304Swpauldefaultshell="${DEFAULTSHELL}"
84814304Swpaul
84914304Swpaul# Make sure the user running this program is root. This isn't a security
85014304Swpaul# measure as much as it is a usefull method of reminding the user to
85114262Swpaul# 'su -' before he/she wastes time entering data that won't be saved.
85214262Swpaul#
85314262Swpaulprocowner=${procowner:-`/usr/bin/id -u`}
85414262Swpaulif [ "$procowner" != "0" ]; then
85514262Swpaul	err 'you must be the super-user (uid 0) to use this utility.'
85614262Swpaul	exit 1
85714262Swpaulfi
85814262Swpaul
85914262Swpaul# Overide from our conf file
86014262Swpaul# Quickly go through the commandline line to see if we should read
86114262Swpaul# from our configuration file. The actual parsing of the commandline
86295658Sdes# arguments happens after we read in our configuration file (commandline
86314262Swpaul# should override configuration file).
86414262Swpaul#
86514262Swpaulfor _i in $* ; do
86614262Swpaul	if [ "$_i" = "-N" ]; then
86714262Swpaul		readconfig=
86814304Swpaul		break;
86914304Swpaul	fi
87014304Swpauldone
87114262Swpaulif [ -n "$readconfig" ]; then
87214262Swpaul	# On a long-lived system, the first time this script is run it
87314262Swpaul	# will barf upon reading the configuration file for its perl predecessor.
87414262Swpaul	if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then
87514262Swpaul		[ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1
87614262Swpaul	fi
87714262Swpaulfi 
87814304Swpaul
87914304Swpaul# Proccess command-line options
88014304Swpaul#
88114304Swpaulfor _switch ; do
88214262Swpaul	case $_switch in
88314262Swpaul	-L)
88414262Swpaul		defaultclass="$2"
88514262Swpaul		shift; shift
88614262Swpaul		;;
88714262Swpaul	-C)
88814262Swpaul		configflag=yes
88914262Swpaul		shift
89014262Swpaul		;;
89114262Swpaul	-D)
89295658Sdes		Dflag=yes
89314262Swpaul		shift
89414262Swpaul		;;
89514262Swpaul	-E)
89614262Swpaul		disableflag=yes
89714262Swpaul		shift
89814304Swpaul		;;
89914304Swpaul	-k)
90014304Swpaul		udotdir="$2"
90114262Swpaul		shift; shift
90214262Swpaul		;;
90314262Swpaul	-f)
90414262Swpaul		[ "$2" != "-" ] && infile="$2"
90514262Swpaul		fflag=yes
90614262Swpaul		shift; shift
90714262Swpaul		;;
90814262Swpaul	-g)
90914262Swpaul		defaultLgroup="$2"
91014262Swpaul		shift; shift
91114262Swpaul		;;
91214262Swpaul	-G)
91314262Swpaul		defaultgroups="$2"
91414262Swpaul		shift; shift
91514262Swpaul		;;
91614262Swpaul	-h)
91714262Swpaul		show_usage
91814262Swpaul		exit 0
91914262Swpaul		;;
92014262Swpaul	-d)
92114262Swpaul		homeprefix="$2"
92214262Swpaul		shift; shift
92314262Swpaul		;;
92414262Swpaul	-m)
92514262Swpaul		case "$2" in
92614262Swpaul		[Nn][Oo])
92714262Swpaul			msgflag=
92814262Swpaul			;;
92914262Swpaul		*)
93014262Swpaul			msgflag=yes
93114262Swpaul			msgfile="$2"
93214262Swpaul			;;
93314262Swpaul		esac
93414262Swpaul		shift; shift
93514262Swpaul		;;
93614262Swpaul	-N)
93714262Swpaul		readconfig=
93814262Swpaul		shift
93914262Swpaul		;;
94014262Swpaul	-w)
94114262Swpaul		case "$2" in
94214262Swpaul		no|none|random|yes)
94314262Swpaul			passwdtype=$2
94414262Swpaul			;;
94514262Swpaul		*)
94614262Swpaul			show_usage
94714262Swpaul			exit 1
94814262Swpaul			;;
94914262Swpaul		esac
95014262Swpaul		shift; shift
95114262Swpaul		;;
95214262Swpaul	-q)
95314262Swpaul		quietflag=yes
95414262Swpaul		shift
95514262Swpaul		;;
95614262Swpaul	-s)
95714262Swpaul		defaultshell="`fullpath_from_shell $2`"
95814262Swpaul		shift; shift
95914262Swpaul		;;
96014262Swpaul	-S)
96114262Swpaul		Sflag=yes
96214262Swpaul		shift
96314262Swpaul		;;
96414262Swpaul	-u)
96514262Swpaul		uidstart=$2
96614262Swpaul		shift; shift
96714262Swpaul		;;
96814262Swpaul	esac
96914262Swpauldone
97014262Swpaul
97114262Swpaul# If the -f switch was used, get input from a file. Otherwise,
97214262Swpaul# this is an interactive session.
97314262Swpaul#
97414262Swpaulif [ -n "$fflag" ]; then
97514262Swpaul	if [ -z "$infile" ]; then
97614262Swpaul		input_from_file
97714262Swpaul	elif [ -n "$infile" ]; then
97814262Swpaul		if [ -r "$infile" ]; then
97914262Swpaul			input_from_file < $infile
98014262Swpaul		else
98114262Swpaul			err "File ($infile) is unreadable or does not exist."
98214262Swpaul		fi
98314262Swpaul	fi
98414262Swpaulelse
98514262Swpaul	input_interactive
98614262Swpaul	while : ; do
987		if [ -z "$configflag" ]; then
988			echo -n "Add another user? (yes/no): "
989		else
990			echo -n "Re-edit the default configuration? (yes/no): "
991		fi
992		read _input
993		case $_input in
994		[Yy][Ee][Ss]|[Yy][Ee]|[Yy])
995			uidstart=`get_nextuid $uidstart`
996			input_interactive
997			continue
998			;;
999		[Nn][Oo]|[Nn])
1000			echo "Goodbye!"
1001			;;
1002		*)
1003			continue
1004			;;
1005		esac
1006		break
1007	done
1008fi
1009