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