adduser.sh revision 168651
136270Swpaul#!/bin/sh 236270Swpaul# 336270Swpaul# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved. 436270Swpaul# 536270Swpaul# Redistribution and use in source and binary forms, with or without 636270Swpaul# modification, are permitted provided that the following conditions 736270Swpaul# are met: 836270Swpaul# 1. Redistributions of source code must retain the above copyright 936270Swpaul# notice, this list of conditions and the following disclaimer. 1036270Swpaul# 2. Redistributions in binary form must reproduce the above copyright 1136270Swpaul# notice, this list of conditions and the following disclaimer in the 1236270Swpaul# documentation and/or other materials provided with the distribution. 1336270Swpaul# 1436270Swpaul# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1536270Swpaul# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1636270Swpaul# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1736270Swpaul# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1836270Swpaul# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1936270Swpaul# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2036270Swpaul# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2136270Swpaul# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2236270Swpaul# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2336270Swpaul# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2436270Swpaul# 2536270Swpaul# Email: Mike Makonnen <mtm@FreeBSD.Org> 2636270Swpaul# 2736270Swpaul# $FreeBSD: head/usr.sbin/adduser/adduser.sh 168651 2007-04-12 07:38:26Z mtm $ 2836270Swpaul# 2936270Swpaul 3036270Swpaul# err msg 3136270Swpaul# Display $msg on stderr, unless we're being quiet. 3250477Speter# 3336270Swpaulerr() { 3436270Swpaul if [ -z "$quietflag" ]; then 3536270Swpaul echo 1>&2 ${THISCMD}: ERROR: $* 3636270Swpaul fi 3736270Swpaul} 3836270Swpaul 3936270Swpaul# info msg 4036270Swpaul# Display $msg on stdout, unless we're being quiet. 4139583Swpaul# 4236270Swpaulinfo() { 4336270Swpaul if [ -z "$quietflag" ]; then 4436270Swpaul echo ${THISCMD}: INFO: $* 4536270Swpaul fi 4639583Swpaul} 4736270Swpaul 4836270Swpaul# get_nextuid 4936270Swpaul# Output the value of $_uid if it is available for use. If it 5036270Swpaul# is not, output the value of the next higher uid that is available. 5136270Swpaul# If a uid is not specified, output the first available uid, as indicated 5236270Swpaul# by pw(8). 5336270Swpaul# 5436270Swpaulget_nextuid () { 5536270Swpaul _uid=$1 5636270Swpaul _nextuid= 5736270Swpaul 5839583Swpaul if [ -z "$_uid" ]; then 5936270Swpaul _nextuid="`${PWCMD} usernext | cut -f1 -d:`" 6036270Swpaul else 6136270Swpaul while : ; do 6236270Swpaul ${PWCMD} usershow $_uid > /dev/null 2>&1 6336270Swpaul if [ ! "$?" -eq 0 ]; then 6436270Swpaul _nextuid=$_uid 6536270Swpaul break 6639583Swpaul fi 6739583Swpaul _uid=$(($_uid + 1)) 6839583Swpaul done 6939583Swpaul fi 7039583Swpaul echo $_nextuid 7139583Swpaul} 7239583Swpaul 7339583Swpaul# show_usage 7439583Swpaul# Display usage information for this utility. 7536270Swpaul# 7636270Swpaulshow_usage() { 7739583Swpaul echo "usage: ${THISCMD} [options]" 7836270Swpaul echo " options may include:" 7936270Swpaul echo " -C save to the configuration file only" 8036270Swpaul echo " -D do not attempt to create the home directory" 8136270Swpaul echo " -E disable this account after creation" 8236270Swpaul echo " -G additional groups to add accounts to" 8336270Swpaul echo " -L login class of the user" 8436270Swpaul echo " -N do not read configuration file" 8536270Swpaul echo " -S a nonexistent shell is not an error" 8636270Swpaul echo " -d home directory" 8736270Swpaul echo " -f file from which input will be received" 8836270Swpaul echo " -g default login group" 8936270Swpaul echo " -h display this usage message" 9036270Swpaul echo " -k path to skeleton home directory" 9136270Swpaul echo " -m user welcome message file" 9236270Swpaul echo " -q absolute minimal user feedback" 9336270Swpaul echo " -s shell" 9436270Swpaul echo " -u uid to start at" 9536270Swpaul echo " -w password type: no, none, yes or random" 9636270Swpaul} 9736270Swpaul 9836270Swpaul# valid_shells 9936270Swpaul# Outputs a list of valid shells from /etc/shells. Only the 10036270Swpaul# basename of the shell is output. 10136270Swpaul# 10236270Swpaulvalid_shells() { 10336270Swpaul _prefix= 10436270Swpaul cat ${ETCSHELLS} | 10536270Swpaul while read _path _junk ; do 10636270Swpaul case $_path in 10736270Swpaul \#*|'') 10836270Swpaul ;; 10936270Swpaul *) 11036270Swpaul echo -n "${_prefix}`basename $_path`" 11136270Swpaul _prefix=' ' 11236270Swpaul ;; 11336270Swpaul esac 11436270Swpaul done 11536270Swpaul 11636270Swpaul # /usr/sbin/nologin is a special case 11736270Swpaul [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}" 11836270Swpaul} 11936270Swpaul 12036270Swpaul# fullpath_from_shell shell 12136270Swpaul# Given $shell, which is either the full path to a shell or 12236270Swpaul# the basename component of a valid shell, get the 12336270Swpaul# full path to the shell from the /etc/shells file. 12436270Swpaul# 12536270Swpaulfullpath_from_shell() { 12636270Swpaul _shell=$1 12736270Swpaul [ -z "$_shell" ] && return 1 12836270Swpaul 12936270Swpaul # /usr/sbin/nologin is a special case; it needs to be handled 13036270Swpaul # before the cat | while loop, since a 'return' from within 13136270Swpaul # a subshell will not terminate the function's execution, and 13236270Swpaul # the path to the nologin shell might be printed out twice. 13336270Swpaul # 13436270Swpaul if [ "$_shell" = "${NOLOGIN}" -o \ 13536270Swpaul "$_shell" = "${NOLOGIN_PATH}" ]; then 13636270Swpaul echo ${NOLOGIN_PATH} 13736270Swpaul return 0; 13836270Swpaul fi 13936270Swpaul 14036270Swpaul cat ${ETCSHELLS} | 14136270Swpaul while read _path _junk ; do 14236270Swpaul case "$_path" in 14336270Swpaul \#*|'') 14436270Swpaul ;; 14536270Swpaul *) 14636270Swpaul if [ "$_path" = "$_shell" -o \ 14736270Swpaul "`basename $_path`" = "$_shell" ]; then 14836270Swpaul echo $_path 14936270Swpaul return 0 15036270Swpaul fi 15136270Swpaul ;; 15236270Swpaul esac 15336270Swpaul done 15436270Swpaul 15536270Swpaul return 1 15636270Swpaul} 15736270Swpaul 15836270Swpaul# shell_exists shell 15936270Swpaul# If the given shell is listed in ${ETCSHELLS} or it is 16036270Swpaul# the nologin shell this function will return 0. 16136270Swpaul# Otherwise, it will return 1. If shell is valid but 16236270Swpaul# the path is invalid or it is not executable it 16336270Swpaul# will emit an informational message saying so. 16436270Swpaul# 16536270Swpaulshell_exists() 16636270Swpaul{ 16736270Swpaul _sh="$1" 16836270Swpaul _shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1" 16936270Swpaul 17036270Swpaul if ! eval $_shellchk; then 17136270Swpaul # The nologin shell is not listed in /etc/shells. 17236270Swpaul if [ "$_sh" != "${NOLOGIN_PATH}" ]; then 17336270Swpaul err "Invalid shell ($_sh) for user $username." 17436270Swpaul return 1 17536270Swpaul fi 17636270Swpaul fi 17736270Swpaul ! [ -x "$_sh" ] && 17836270Swpaul info "The shell ($_sh) does not exist or is not executable." 17936270Swpaul 18036270Swpaul return 0 18136270Swpaul} 18236270Swpaul 18336270Swpaul# save_config 18436270Swpaul# Save some variables to a configuration file. 18536270Swpaul# Note: not all script variables are saved, only those that 18636270Swpaul# it makes sense to save. 18736270Swpaul# 18836270Swpaulsave_config() { 18936270Swpaul echo "# Configuration file for adduser(8)." > ${ADDUSERCONF} 19036270Swpaul echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF} 19136270Swpaul echo "# Last Modified on `${DATECMD}`." >> ${ADDUSERCONF} 19236270Swpaul echo '' >> ${ADDUSERCONF} 19336270Swpaul echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF} 19436270Swpaul echo "defaultclass=$uclass" >> ${ADDUSERCONF} 19536270Swpaul echo "defaultgroups=$ugroups" >> ${ADDUSERCONF} 19636270Swpaul echo "passwdtype=$passwdtype" >> ${ADDUSERCONF} 19736270Swpaul echo "homeprefix=$homeprefix" >> ${ADDUSERCONF} 19836270Swpaul echo "defaultshell=$ushell" >> ${ADDUSERCONF} 19936270Swpaul echo "udotdir=$udotdir" >> ${ADDUSERCONF} 20045155Swpaul echo "msgfile=$msgfile" >> ${ADDUSERCONF} 20145155Swpaul echo "disableflag=$disableflag" >> ${ADDUSERCONF} 20245155Swpaul} 20348992Swpaul 20448992Swpaul# add_user 20548992Swpaul# Add a user to the user database. If the user chose to send a welcome 20636270Swpaul# message or lock the account, do so. 20750462Swpaul# 20850462Swpauladd_user() { 20950462Swpaul 21036270Swpaul # Is this a configuration run? If so, don't modify user database. 21136270Swpaul # 21236270Swpaul if [ -n "$configflag" ]; then 21339957Swpaul save_config 21439957Swpaul return 21539957Swpaul fi 21639957Swpaul 21739957Swpaul _uid= 21839957Swpaul _name= 21939957Swpaul _comment= 22036270Swpaul _gecos= 22136270Swpaul _home= 22259758Speter _group= 22359758Speter _grouplist= 22451089Speter _shell= 22550462Swpaul _class= 22650462Swpaul _dotdir= 22741591Sarchie _expire= 22841591Sarchie _pwexpire= 22950477Speter _passwd= 23036270Swpaul _upasswd= 23136270Swpaul _passwdmethod= 23236270Swpaul 23336270Swpaul _name="-n '$username'" 23436270Swpaul [ -n "$uuid" ] && _uid='-u "$uuid"' 23536270Swpaul [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"' 23636270Swpaul [ -n "$ugroups" ] && _grouplist='-G "$ugroups"' 23736270Swpaul [ -n "$ushell" ] && _shell='-s "$ushell"' 23836270Swpaul [ -n "$uclass" ] && _class='-L "$uclass"' 23936270Swpaul [ -n "$ugecos" ] && _comment='-c "$ugecos"' 24036270Swpaul [ -n "$udotdir" ] && _dotdir='-k "$udotdir"' 24136270Swpaul [ -n "$uexpire" ] && _expire='-e "$uexpire"' 24236270Swpaul [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"' 24336270Swpaul if [ -z "$Dflag" -a -n "$uhome" ]; then 24436270Swpaul # The /nonexistent home directory is special. It 24536270Swpaul # means the user has no home directory. 24636270Swpaul if [ "$uhome" = "$NOHOME" ]; then 24736270Swpaul _home='-d "$uhome"' 24836270Swpaul else 24936270Swpaul _home='-m -d "$uhome"' 25036270Swpaul fi 25136270Swpaul elif [ -n "$Dflag" -a -n "$uhome" ]; then 25236270Swpaul _home='-d "$uhome"' 25337626Swpaul fi 25437626Swpaul case $passwdtype in 25537626Swpaul no) 25637626Swpaul _passwdmethod="-w no" 25737626Swpaul _passwd="-h -" 25837626Swpaul ;; 25937626Swpaul yes) 26037626Swpaul # Note on processing the password: The outer double quotes 26137626Swpaul # make literal everything except ` and \ and $. 26237626Swpaul # The outer single quotes make literal ` and $. 26337626Swpaul # We can ensure the \ isn't treated specially by specifying 26437626Swpaul # the -r switch to the read command used to obtain the input. 26536270Swpaul # 26636270Swpaul _passwdmethod="-w yes" 26736270Swpaul _passwd="-h 0" 26848992Swpaul _upasswd='echo "$upass" |' 26948992Swpaul ;; 27048992Swpaul none) 27136270Swpaul _passwdmethod="-w none" 27236270Swpaul ;; 27336270Swpaul random) 27436270Swpaul _passwdmethod="-w random" 27536270Swpaul ;; 27636270Swpaul esac 27736270Swpaul 27837626Swpaul _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment" 27937626Swpaul _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd" 28036270Swpaul _pwcmd="$_pwcmd $_expire $_pwexpire" 28136270Swpaul 28236270Swpaul if ! _output=`eval $_pwcmd` ; then 28336270Swpaul err "There was an error adding user ($username)." 28436270Swpaul return 1 28536270Swpaul else 28636735Sdfr info "Successfully added ($username) to the user database." 28736270Swpaul if [ "random" = "$passwdtype" ]; then 28836270Swpaul randompass="$_output" 28936270Swpaul info "Password for ($username) is: $randompass" 29048992Swpaul fi 29136270Swpaul fi 29236270Swpaul 29336270Swpaul if [ -n "$disableflag" ]; then 29441656Swpaul if ${PWCMD} lock $username ; then 29539583Swpaul info "Account ($username) is locked." 29641656Swpaul else 29739583Swpaul info "Account ($username) could NOT be locked." 29836270Swpaul fi 29939583Swpaul fi 30039583Swpaul 30139583Swpaul _line= 30239583Swpaul _owner= 30350462Swpaul _perms= 30450462Swpaul if [ -n "$msgflag" ]; then 30550462Swpaul [ -r "$msgfile" ] && { 30636270Swpaul # We're evaluating the contents of an external file. 30736270Swpaul # Let's not open ourselves up for attack. _perms will 30841656Swpaul # be empty if it's writeable only by the owner. _owner 30936270Swpaul # will *NOT* be empty if the file is owned by root. 31041656Swpaul # 31139583Swpaul _dir="`dirname $msgfile`" 31250468Swpaul _file="`basename $msgfile`" 31336270Swpaul _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune` 31436270Swpaul _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune` 31536270Swpaul if [ -z "$_owner" -o -n "$_perms" ]; then 31641656Swpaul err "The message file ($msgfile) may be writeable only by root." 31741656Swpaul return 1 31841656Swpaul fi 31941656Swpaul cat "$msgfile" | 32041656Swpaul while read _line ; do 32141656Swpaul eval echo "$_line" 32241656Swpaul done | ${MAILCMD} -s"Welcome" ${username} 32341656Swpaul info "Sent welcome message to ($username)." 32441656Swpaul } 32541656Swpaul fi 32639583Swpaul} 32749010Swpaul 32849010Swpaul# get_user 32949010Swpaul# Reads username of the account from standard input or from a global 33049010Swpaul# variable containing an account line from a file. The username is 33149010Swpaul# required. If this is an interactive session it will prompt in 33249010Swpaul# a loop until a username is entered. If it is batch processing from 33349010Swpaul# a file it will output an error message and return to the caller. 33449010Swpaul# 33548992Swpaulget_user() { 33648992Swpaul _input= 33748992Swpaul 33848992Swpaul # No need to take down user names if this is a configuration saving run. 33948992Swpaul [ -n "$configflag" ] && return 34048992Swpaul 34150462Swpaul while : ; do 34250462Swpaul if [ -z "$fflag" ]; then 34350462Swpaul echo -n "Username: " 34450462Swpaul read _input 34550462Swpaul else 34650462Swpaul _input="`echo "$fileline" | cut -f1 -d:`" 34750462Swpaul fi 34850462Swpaul 34950462Swpaul # There *must* be a username, and it must not exist. If 35050462Swpaul # this is an interactive session give the user an 35148992Swpaul # opportunity to retry. 35248992Swpaul # 35348992Swpaul if [ -z "$_input" ]; then 35448992Swpaul err "You must enter a username!" 35551455Swpaul [ -z "$fflag" ] && continue 35648992Swpaul fi 35748992Swpaul ${PWCMD} usershow $_input > /dev/null 2>&1 35848992Swpaul if [ "$?" -eq 0 ]; then 35948992Swpaul err "User exists!" 36048992Swpaul [ -z "$fflag" ] && continue 36148992Swpaul fi 36251533Swpaul break 36351473Swpaul done 36448992Swpaul username="$_input" 36539583Swpaul} 36641656Swpaul 36741656Swpaul# get_gecos 36839583Swpaul# Reads extra information about the user. Can be used both in interactive 36939583Swpaul# and batch (from file) mode. 37039583Swpaul# 37139583Swpaulget_gecos() { 37239583Swpaul _input= 37339583Swpaul 37441656Swpaul # No need to take down additional user information for a configuration run. 37541656Swpaul [ -n "$configflag" ] && return 37639583Swpaul 37739583Swpaul if [ -z "$fflag" ]; then 37839583Swpaul echo -n "Full name: " 37939583Swpaul read _input 38039583Swpaul else 38139583Swpaul _input="`echo "$fileline" | cut -f7 -d:`" 38241656Swpaul fi 38341656Swpaul ugecos="$_input" 38439583Swpaul} 38539583Swpaul 38639583Swpaul# get_shell 38739583Swpaul# Get the account's shell. Works in interactive and batch mode. It 38839583Swpaul# accepts either the base name of the shell or the full path. 38939583Swpaul# If an invalid shell is entered it will simply use the default shell. 39041656Swpaul# 39141656Swpaulget_shell() { 39241656Swpaul _input= 39339583Swpaul _fullpath= 39439583Swpaul ushell="$defaultshell" 39539583Swpaul 39639583Swpaul # Make sure the current value of the shell is a valid one 39739583Swpaul if [ -z "$Sflag" ]; then 39839583Swpaul if ! shell_exists $ushell ; then 39939583Swpaul info "Using default shell ${defaultshell}." 40041656Swpaul ushell="$defaultshell" 40141656Swpaul fi 40241656Swpaul fi 40339583Swpaul 40439583Swpaul if [ -z "$fflag" ]; then 40539583Swpaul echo -n "Shell ($shells) [`basename $ushell`]: " 40639583Swpaul read _input 40739583Swpaul else 40839583Swpaul _input="`echo "$fileline" | cut -f9 -d:`" 40939583Swpaul fi 41041656Swpaul if [ -n "$_input" ]; then 41141656Swpaul if [ -n "$Sflag" ]; then 41241656Swpaul ushell="$_input" 41339583Swpaul else 41439583Swpaul _fullpath=`fullpath_from_shell $_input` 41539583Swpaul if [ -n "$_fullpath" ]; then 41639583Swpaul ushell="$_fullpath" 41739583Swpaul else 41839583Swpaul err "Invalid shell ($_input) for user $username." 41939583Swpaul info "Using default shell ${defaultshell}." 42041656Swpaul ushell="$defaultshell" 42141656Swpaul fi 42241656Swpaul fi 42339583Swpaul fi 42439583Swpaul} 42539583Swpaul 42639583Swpaul# get_homedir 42739583Swpaul# Reads the account's home directory. Used both with interactive input 42839583Swpaul# and batch input. 42939583Swpaul# 43039583Swpaulget_homedir() { 43139583Swpaul _input= 43239583Swpaul if [ -z "$fflag" ]; then 43339583Swpaul echo -n "Home directory [${homeprefix}/${username}]: " 43439583Swpaul read _input 43541656Swpaul else 43641656Swpaul _input="`echo "$fileline" | cut -f8 -d:`" 43741656Swpaul fi 43839583Swpaul 43939583Swpaul if [ -n "$_input" ]; then 44039583Swpaul uhome="$_input" 44139583Swpaul # if this is a configuration run, then user input is the home 44239583Swpaul # directory prefix. Otherwise it is understood to 44339583Swpaul # be $prefix/$user 44439583Swpaul # 44539583Swpaul [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome" 44639583Swpaul else 44739583Swpaul uhome="${homeprefix}/${username}" 44839583Swpaul fi 44939583Swpaul} 45041656Swpaul 45141656Swpaul# get_uid 45241656Swpaul# Reads a numeric userid in an interactive or batch session. Automatically 45339583Swpaul# allocates one if it is not specified. 45439583Swpaul# 45539583Swpaulget_uid() { 45639583Swpaul if [ -z "$uuid" ]; then 45739583Swpaul uuid=${uidstart} 45839583Swpaul fi 45939583Swpaul 46039583Swpaul _input= 46139583Swpaul _prompt= 46239583Swpaul 46339583Swpaul # No need to take down uids for a configuration saving run. 46439583Swpaul [ -n "$configflag" ] && return 46541656Swpaul 46641656Swpaul if [ -n "$uuid" ]; then 46741656Swpaul _prompt="Uid [$uuid]: " 46839583Swpaul else 46939583Swpaul _prompt="Uid (Leave empty for default): " 47039583Swpaul fi 47139583Swpaul if [ -z "$fflag" ]; then 47239583Swpaul echo -n "$_prompt" 47339583Swpaul read _input 47439583Swpaul else 47539583Swpaul _input="`echo "$fileline" | cut -f2 -d:`" 47639583Swpaul fi 47739583Swpaul 47839583Swpaul [ -n "$_input" ] && uuid=$_input 47936270Swpaul uuid=`get_nextuid $uuid` 48036270Swpaul uidstart=$uuid 48136270Swpaul} 48239583Swpaul 48339583Swpaul# get_class 48441656Swpaul# Reads login class of account. Can be used in interactive or batch mode. 48536270Swpaul# 48636270Swpaulget_class() { 48736270Swpaul uclass="$defaultclass" 48836270Swpaul _input= 48936270Swpaul _class=${uclass:-"default"} 49036270Swpaul 49139583Swpaul if [ -z "$fflag" ]; then 49236270Swpaul echo -n "Login class [$_class]: " 49336270Swpaul read _input 49436270Swpaul else 49536270Swpaul _input="`echo "$fileline" | cut -f4 -d:`" 49636270Swpaul fi 49736270Swpaul 49839583Swpaul [ -n "$_input" ] && uclass="$_input" 49936270Swpaul} 50039583Swpaul 50136270Swpaul# get_logingroup 50239583Swpaul# Reads user's login group. Can be used in both interactive and batch 50339583Swpaul# modes. The specified value can be a group name or its numeric id. 50439583Swpaul# This routine leaves the field blank if nothing is provided and 50539583Swpaul# a default login group has not been set. The pw(8) command 50636270Swpaul# will then provide a login group with the same name as the username. 50736270Swpaul# 50836270Swpaulget_logingroup() { 50936270Swpaul ulogingroup="$defaultLgroup" 51036270Swpaul _input= 51139583Swpaul 51236270Swpaul if [ -z "$fflag" ]; then 51336270Swpaul echo -n "Login group [${ulogingroup:-$username}]: " 51436270Swpaul read _input 51536270Swpaul else 51639583Swpaul _input="`echo "$fileline" | cut -f3 -d:`" 51739583Swpaul fi 51839583Swpaul 51936270Swpaul # Pw(8) will use the username as login group if it's left empty 52036270Swpaul [ -n "$_input" ] && ulogingroup="$_input" 52136270Swpaul} 52236270Swpaul 52336270Swpaul# get_groups 52436270Swpaul# Read additional groups for the user. It can be used in both interactive 52536270Swpaul# and batch modes. 52639583Swpaul# 52739583Swpaulget_groups() { 52841656Swpaul ugroups="$defaultgroups" 52936270Swpaul _input= 53036270Swpaul _group=${ulogingroup:-"${username}"} 53136270Swpaul 53236270Swpaul if [ -z "$configflag" ]; then 53336270Swpaul [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username" 53439583Swpaul [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: " 53539583Swpaul else 53636270Swpaul [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: " 53739583Swpaul fi 53836270Swpaul read _input 53936270Swpaul 54036270Swpaul [ -n "$_input" ] && ugroups="$_input" 54139583Swpaul} 54239583Swpaul 54339583Swpaul# get_expire_dates 54436270Swpaul# Read expiry information for the account and also for the password. This 54539583Swpaul# routine is used only from batch processing mode. 54636270Swpaul# 54736270Swpaulget_expire_dates() { 54836270Swpaul upwexpire="`echo "$fileline" | cut -f5 -d:`" 54936270Swpaul uexpire="`echo "$fileline" | cut -f6 -d:`" 55039583Swpaul} 55139583Swpaul 55239583Swpaul# get_password 55336270Swpaul# Read the password in batch processing mode. The password field matters 55439583Swpaul# only when the password type is "yes" or "random". If the field is empty and the 55536270Swpaul# password type is "yes", then it assumes the account has an empty passsword 55636270Swpaul# and changes the password type accordingly. If the password type is "random" 55736270Swpaul# and the password field is NOT empty, then it assumes the account will NOT 55836270Swpaul# have a random password and set passwdtype to "yes." 55936270Swpaul# 56036270Swpaulget_password() { 56139583Swpaul # We may temporarily change a password type. Make sure it's changed 56239583Swpaul # back to whatever it was before we process the next account. 56339583Swpaul # 56436270Swpaul [ -n "$savedpwtype" ] && { 56539583Swpaul passwdtype=$savedpwtype 56636270Swpaul savedpwtype= 56736270Swpaul } 56836270Swpaul 56936270Swpaul # There may be a ':' in the password 57039583Swpaul upass=${fileline#*:*:*:*:*:*:*:*:*:} 57136270Swpaul 57239583Swpaul if [ -z "$upass" ]; then 57339583Swpaul case $passwdtype in 57439583Swpaul yes) 57536270Swpaul # if it's empty, assume an empty password 57639583Swpaul passwdtype=none 57736501Swpaul savedpwtype=yes 57836270Swpaul ;; 57936270Swpaul esac 58036270Swpaul else 58136270Swpaul case $passwdtype in 58236270Swpaul random) 58336270Swpaul passwdtype=yes 58436270Swpaul savedpwtype=random 58536270Swpaul ;; 58636270Swpaul esac 58736270Swpaul fi 58836270Swpaul} 58936270Swpaul 59036270Swpaul# input_from_file 59139583Swpaul# Reads a line of account information from standard input and 59239583Swpaul# adds it to the user database. 59339583Swpaul# 59439583Swpaulinput_from_file() { 59539583Swpaul _field= 59639583Swpaul 59739583Swpaul while read -r fileline ; do 59839583Swpaul case "$fileline" in 59936270Swpaul \#*|'') 60039583Swpaul ;; 60139583Swpaul *) 60239583Swpaul get_user || continue 60339583Swpaul get_gecos 60439583Swpaul get_uid 60539583Swpaul get_logingroup 60639583Swpaul get_class 60739583Swpaul get_shell 60839583Swpaul get_homedir 60939583Swpaul get_password 61039583Swpaul get_expire_dates 61139583Swpaul 61239583Swpaul add_user 61339583Swpaul ;; 61439583Swpaul esac 61539583Swpaul done 61636270Swpaul} 61736270Swpaul 61839583Swpaul# input_interactive 61936270Swpaul# Prompts for user information interactively, and commits to 62036270Swpaul# the user database. 62139583Swpaul# 62239583Swpaulinput_interactive() { 62336270Swpaul 62436270Swpaul _disable= 62536270Swpaul _pass= 62636270Swpaul _passconfirm= 62736270Swpaul _random="no" 62839583Swpaul _emptypass="no" 62939583Swpaul _usepass="yes" 63036270Swpaul _logingroup_ok="no" 63136270Swpaul _groups_ok="no" 63236270Swpaul case $passwdtype in 63336270Swpaul none) 63436270Swpaul _emptypass="yes" 63536270Swpaul _usepass="yes" 63639583Swpaul ;; 63736270Swpaul no) 63839583Swpaul _usepass="no" 63936270Swpaul ;; 64039583Swpaul random) 64136270Swpaul _random="yes" 64239583Swpaul ;; 64336270Swpaul esac 64436270Swpaul 64536270Swpaul get_user 64639583Swpaul get_gecos 64739583Swpaul get_uid 64836270Swpaul 64936270Swpaul # The case where group = user is handled elsewhere, so 65036270Swpaul # validate any other groups the user is invited to. 65167087Swpaul until [ "$_logingroup_ok" = yes ]; do 65236270Swpaul get_logingroup 65336270Swpaul _logingroup_ok=yes 65467087Swpaul if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then 65536270Swpaul if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then 65639583Swpaul echo "Group $ulogingroup does not exist!" 65736270Swpaul _logingroup_ok=no 65836270Swpaul fi 65936270Swpaul fi 66036270Swpaul done 66136270Swpaul until [ "$_groups_ok" = yes ]; do 66236270Swpaul get_groups 66336270Swpaul _groups_ok=yes 66436270Swpaul for i in $ugroups; do 66536270Swpaul if [ "$username" != "$i" ]; then 66636270Swpaul if ! ${PWCMD} show group $i > /dev/null 2>&1; then 66736270Swpaul echo "Group $i does not exist!" 66836270Swpaul _groups_ok=no 66939583Swpaul fi 67036270Swpaul fi 67139583Swpaul done 67236270Swpaul done 67336270Swpaul 67436270Swpaul get_class 67536270Swpaul get_shell 67636270Swpaul get_homedir 67739583Swpaul 67836270Swpaul while : ; do 67936270Swpaul echo -n "Use password-based authentication? [$_usepass]: " 68036270Swpaul read _input 68136270Swpaul [ -z "$_input" ] && _input=$_usepass 68239583Swpaul case $_input in 68339583Swpaul [Nn][Oo]|[Nn]) 68439583Swpaul passwdtype="no" 68539583Swpaul ;; 68636270Swpaul [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 68736270Swpaul while : ; do 68836270Swpaul echo -n "Use an empty password? (yes/no) [$_emptypass]: " 68936270Swpaul read _input 69039583Swpaul [ -n "$_input" ] && _emptypass=$_input 69136270Swpaul case $_emptypass in 69236270Swpaul [Nn][Oo]|[Nn]) 69339583Swpaul echo -n "Use a random password? (yes/no) [$_random]: " 69439583Swpaul read _input 69536270Swpaul [ -n "$_input" ] && _random="$_input" 69636270Swpaul case $_random in 69739583Swpaul [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 69839583Swpaul passwdtype="random" 69936270Swpaul break 70036270Swpaul ;; 70139583Swpaul esac 70236270Swpaul passwdtype="yes" 70336270Swpaul [ -n "$configflag" ] && break 70436270Swpaul trap 'stty echo; exit' 0 1 2 3 15 70536270Swpaul stty -echo 70636270Swpaul echo -n "Enter password: " 70736270Swpaul read -r upass 70836270Swpaul echo'' 70939583Swpaul echo -n "Enter password again: " 71039583Swpaul read -r _passconfirm 71136270Swpaul echo '' 71236270Swpaul stty echo 71336270Swpaul # if user entered a blank password 71436270Swpaul # explicitly ask again. 71536270Swpaul [ -z "$upass" -a -z "$_passconfirm" ] \ 71639583Swpaul && continue 71736270Swpaul ;; 71839583Swpaul [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 71936270Swpaul passwdtype="none" 72036270Swpaul break; 72139583Swpaul ;; 72236270Swpaul *) 72336270Swpaul # invalid answer; repeat the loop 72436270Swpaul continue 72536270Swpaul ;; 72639583Swpaul esac 72739583Swpaul if [ "$upass" != "$_passconfirm" ]; then 72836270Swpaul echo "Passwords did not match!" 72936270Swpaul continue 73036270Swpaul fi 73139583Swpaul break 73236270Swpaul done 73336270Swpaul ;; 73467087Swpaul *) 73536270Swpaul # invalid answer; repeat loop 73636270Swpaul continue 73736270Swpaul ;; 73836270Swpaul esac 73936270Swpaul break; 74036270Swpaul done 74139583Swpaul _disable=${disableflag:-"no"} 74239583Swpaul while : ; do 74336270Swpaul echo -n "Lock out the account after creation? [$_disable]: " 74436270Swpaul read _input 74536270Swpaul [ -z "$_input" ] && _input=$_disable 74636270Swpaul case $_input in 74736270Swpaul [Nn][Oo]|[Nn]) 74867087Swpaul disableflag= 74967087Swpaul ;; 75039583Swpaul [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 75136270Swpaul disableflag=yes 75236270Swpaul ;; 75336270Swpaul *) 75436270Swpaul # invalid answer; repeat loop 75536270Swpaul continue 75636270Swpaul ;; 75736270Swpaul esac 75836270Swpaul break 75936270Swpaul done 76036270Swpaul 76136270Swpaul # Display the information we have so far and prompt to 76236270Swpaul # commit it. 76339583Swpaul # 76436270Swpaul _disable=${disableflag:-"no"} 76539583Swpaul [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username 76636270Swpaul case $passwdtype in 76736270Swpaul yes) 76836270Swpaul _pass='*****' 76936270Swpaul ;; 77036270Swpaul no) 77139583Swpaul _pass='<disabled>' 77236270Swpaul ;; 77339583Swpaul none) 77439583Swpaul _pass='<blank>' 77539583Swpaul ;; 77639583Swpaul random) 77739583Swpaul _pass='<random>' 77839583Swpaul ;; 77936270Swpaul esac 78039583Swpaul [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass" 78139583Swpaul [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype" 78236270Swpaul [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos" 78336270Swpaul [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid" 78436270Swpaul printf "%-10s : %s\n" "Class" "$uclass" 78536270Swpaul printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups" 78639583Swpaul printf "%-10s : %s\n" "Home" "$uhome" 78736270Swpaul printf "%-10s : %s\n" "Shell" "$ushell" 78836270Swpaul printf "%-10s : %s\n" "Locked" "$_disable" 78936270Swpaul while : ; do 79039583Swpaul echo -n "OK? (yes/no): " 79136270Swpaul read _input 79267087Swpaul case $_input in 79336270Swpaul [Nn][Oo]|[Nn]) 79436270Swpaul return 1 79536270Swpaul ;; 79636270Swpaul [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 79750462Swpaul add_user 79850462Swpaul ;; 79950462Swpaul *) 80050462Swpaul continue 80136270Swpaul ;; 80236270Swpaul esac 80336270Swpaul break 80450462Swpaul done 80536270Swpaul return 0 80636270Swpaul} 80750462Swpaul 80836270Swpaul#### END SUBROUTINE DEFINITION #### 80939583Swpaul 81036270SwpaulTHISCMD=`/usr/bin/basename $0` 81136270SwpaulDEFAULTSHELL=/bin/sh 81236270SwpaulADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}" 81336270SwpaulPWCMD="${PWCMD:-/usr/sbin/pw}" 81450462SwpaulMAILCMD="${MAILCMD:-mail}" 81550462SwpaulETCSHELLS="${ETCSHELLS:-/etc/shells}" 81650462SwpaulNOHOME="/nonexistent" 81750462SwpaulNOLOGIN="nologin" 81836270SwpaulNOLOGIN_PATH="/usr/sbin/nologin" 81936270SwpaulGREPCMD="/usr/bin/grep" 82036270SwpaulDATECMD="/bin/date" 82150462Swpaul 82236270Swpaul# Set default values 82336270Swpaul# 82450462Swpaulusername= 82536270Swpauluuid= 82636270Swpauluidstart= 82736270Swpaulugecos= 82839583Swpaululogingroup= 82936270Swpauluclass= 83050462Swpauluhome= 83136270Swpaulupass= 83236270Swpaulushell= 83350462Swpauludotdir=/usr/share/skel 83450462Swpaulugroups= 83550462Swpauluexpire= 83636270Swpaulupwexpire= 83750462Swpaulshells="`valid_shells`" 83836270Swpaulpasswdtype="yes" 83950462Swpaulmsgfile=/etc/adduser.msg 84067087Swpaulmsgflag= 84150462Swpaulquietflag= 84236270Swpaulconfigflag= 84350462Swpaulfflag= 84450462Swpaulinfile= 84536270Swpauldisableflag= 84650462SwpaulDflag= 84736270SwpaulSflag= 84867087Swpaulreadconfig="yes" 84936270Swpaulhomeprefix="/home" 85036270Swpaulrandompass= 85136270Swpaulfileline= 85236270Swpaulsavedpwtype= 85336270Swpauldefaultclass= 85450462SwpauldefaultLgroup= 85536270Swpauldefaultgroups= 85636270Swpauldefaultshell="${DEFAULTSHELL}" 85736270Swpaul 85836270Swpaul# Make sure the user running this program is root. This isn't a security 85936270Swpaul# measure as much as it is a usefull method of reminding the user to 86050462Swpaul# 'su -' before he/she wastes time entering data that won't be saved. 86150462Swpaul# 86236270Swpaulprocowner=${procowner:-`/usr/bin/id -u`} 86350462Swpaulif [ "$procowner" != "0" ]; then 86436270Swpaul err 'you must be the super-user (uid 0) to use this utility.' 86550462Swpaul exit 1 86639583Swpaulfi 86736270Swpaul 86850462Swpaul# Overide from our conf file 86939583Swpaul# Quickly go through the commandline line to see if we should read 87036270Swpaul# from our configuration file. The actual parsing of the commandline 87136270Swpaul# arguments happens after we read in our configuration file (commandline 87236270Swpaul# should override configuration file). 87336270Swpaul# 87436270Swpaulfor _i in $* ; do 87536270Swpaul if [ "$_i" = "-N" ]; then 87636464Swpaul readconfig= 87736464Swpaul break; 87836464Swpaul fi 87936464Swpauldone 88036464Swpaulif [ -n "$readconfig" ]; then 88136464Swpaul # On a long-lived system, the first time this script is run it 88236464Swpaul # will barf upon reading the configuration file for its perl predecessor. 88336464Swpaul if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then 88436464Swpaul [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1 88536270Swpaul fi 88641656Swpaulfi 88736270Swpaul 88837626Swpaul# Proccess command-line options 88936270Swpaul# 89036464Swpaulfor _switch ; do 89136464Swpaul case $_switch in 89236464Swpaul -L) 89336270Swpaul defaultclass="$2" 89436270Swpaul shift; shift 89539583Swpaul ;; 89639583Swpaul -C) 89739583Swpaul configflag=yes 89839583Swpaul shift 89939583Swpaul ;; 90039583Swpaul -D) 90139583Swpaul Dflag=yes 90239583Swpaul shift 90339583Swpaul ;; 90441656Swpaul -E) 90539583Swpaul disableflag=yes 90639583Swpaul shift 90739583Swpaul ;; 90839583Swpaul -k) 90939583Swpaul udotdir="$2" 91039583Swpaul shift; shift 91139583Swpaul ;; 91239583Swpaul -f) 91339583Swpaul [ "$2" != "-" ] && infile="$2" 91439583Swpaul fflag=yes 91539583Swpaul shift; shift 91639583Swpaul ;; 91739583Swpaul -g) 91839583Swpaul defaultLgroup="$2" 91939583Swpaul shift; shift 92039583Swpaul ;; 92139583Swpaul -G) 92239583Swpaul defaultgroups="$2" 92339583Swpaul shift; shift 92439583Swpaul ;; 92539583Swpaul -h) 92639583Swpaul show_usage 92739583Swpaul exit 0 92839583Swpaul ;; 92939583Swpaul -d) 93039583Swpaul homeprefix="$2" 93139583Swpaul shift; shift 93239583Swpaul ;; 93339583Swpaul -m) 93436270Swpaul case "$2" in 93536270Swpaul [Nn][Oo]) 93636270Swpaul msgflag= 93736270Swpaul ;; 93836270Swpaul *) 93939583Swpaul msgflag=yes 94036270Swpaul msgfile="$2" 94139583Swpaul ;; 94236270Swpaul esac 94336270Swpaul shift; shift 94439583Swpaul ;; 94539583Swpaul -N) 94641656Swpaul readconfig= 94739583Swpaul shift 94839583Swpaul ;; 94939583Swpaul -w) 95039583Swpaul case "$2" in 95139583Swpaul no|none|random|yes) 95236270Swpaul passwdtype=$2 95336270Swpaul ;; 95436270Swpaul *) 95539583Swpaul show_usage 95639583Swpaul exit 1 95736270Swpaul ;; 95836270Swpaul esac 95939583Swpaul shift; shift 96039583Swpaul ;; 96139583Swpaul -q) 96239583Swpaul quietflag=yes 96339583Swpaul shift 96439583Swpaul ;; 96536270Swpaul -s) 96636270Swpaul defaultshell="`fullpath_from_shell $2`" 96739583Swpaul shift; shift 96839583Swpaul ;; 96939583Swpaul -S) 97039583Swpaul Sflag=yes 97139583Swpaul shift 97239583Swpaul ;; 97339583Swpaul -u) 97439583Swpaul uidstart=$2 97539583Swpaul shift; shift 97639583Swpaul ;; 97739583Swpaul esac 97839583Swpauldone 97936270Swpaul 98036270Swpaul# If the -f switch was used, get input from a file. Otherwise, 98136270Swpaul# this is an interactive session. 98236270Swpaul# 98336270Swpaulif [ -n "$fflag" ]; then 98436317Swpaul if [ -z "$infile" ]; then 98536270Swpaul input_from_file 98636270Swpaul elif [ -n "$infile" ]; then 98736270Swpaul if [ -r "$infile" ]; then 98839583Swpaul input_from_file < $infile 98939583Swpaul else 99036270Swpaul err "File ($infile) is unreadable or does not exist." 99136270Swpaul fi 99236270Swpaul fi 99336270Swpaulelse 99439583Swpaul input_interactive 99539583Swpaul while : ; do 99639583Swpaul if [ -z "$configflag" ]; then 99739583Swpaul echo -n "Add another user? (yes/no): " 99839583Swpaul else 99939583Swpaul echo -n "Re-edit the default configuration? (yes/no): " 100050468Swpaul fi 100150468Swpaul read _input 100250468Swpaul case $_input in 100339583Swpaul [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 100439583Swpaul uidstart=`get_nextuid $uidstart` 100550468Swpaul input_interactive 100639583Swpaul continue 100750468Swpaul ;; 100839583Swpaul [Nn][Oo]|[Nn]) 100950468Swpaul echo "Goodbye!" 101039583Swpaul ;; 101150468Swpaul *) 101239583Swpaul continue 101350468Swpaul ;; 101450468Swpaul esac 101539583Swpaul break 101650468Swpaul done 101739583Swpaulfi 101850468Swpaul