adduser.sh revision 167917
1107543Sscottl#!/bin/sh 2107543Sscottl# 3127076Smtm# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved. 4107543Sscottl# 5107543Sscottl# Redistribution and use in source and binary forms, with or without 6107543Sscottl# modification, are permitted provided that the following conditions 7107543Sscottl# are met: 8107543Sscottl# 1. Redistributions of source code must retain the above copyright 9107543Sscottl# notice, this list of conditions and the following disclaimer. 10107543Sscottl# 2. Redistributions in binary form must reproduce the above copyright 11107543Sscottl# notice, this list of conditions and the following disclaimer in the 12107543Sscottl# documentation and/or other materials provided with the distribution. 13107543Sscottl# 14107543Sscottl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15107543Sscottl# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16107543Sscottl# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17107543Sscottl# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18107543Sscottl# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19107543Sscottl# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20107543Sscottl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21107543Sscottl# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22107543Sscottl# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23107543Sscottl# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24107543Sscottl# 25116624Smtm# Email: Mike Makonnen <mtm@FreeBSD.Org> 26107543Sscottl# 27107543Sscottl# $FreeBSD: head/usr.sbin/adduser/adduser.sh 167917 2007-03-26 22:22:10Z le $ 28107543Sscottl# 29107543Sscottl 30107543Sscottl# err msg 31107543Sscottl# Display $msg on stderr, unless we're being quiet. 32107543Sscottl# 33107543Sscottlerr() { 34107543Sscottl if [ -z "$quietflag" ]; then 35107543Sscottl echo 1>&2 ${THISCMD}: ERROR: $* 36107543Sscottl fi 37107543Sscottl} 38107543Sscottl 39107543Sscottl# info msg 40107543Sscottl# Display $msg on stdout, unless we're being quiet. 41107543Sscottl# 42107543Sscottlinfo() { 43107543Sscottl if [ -z "$quietflag" ]; then 44107543Sscottl echo ${THISCMD}: INFO: $* 45107543Sscottl fi 46107543Sscottl} 47107543Sscottl 48107543Sscottl# get_nextuid 49107543Sscottl# Output the value of $_uid if it is available for use. If it 50107543Sscottl# is not, output the value of the next higher uid that is available. 51107543Sscottl# If a uid is not specified, output the first available uid, as indicated 52107543Sscottl# by pw(8). 53107543Sscottl# 54107543Sscottlget_nextuid () { 55107543Sscottl _uid=$1 56107543Sscottl _nextuid= 57107543Sscottl 58107543Sscottl if [ -z "$_uid" ]; then 59107543Sscottl _nextuid="`${PWCMD} usernext | cut -f1 -d:`" 60107543Sscottl else 61107543Sscottl while : ; do 62107543Sscottl ${PWCMD} usershow $_uid > /dev/null 2>&1 63107543Sscottl if [ ! "$?" -eq 0 ]; then 64107543Sscottl _nextuid=$_uid 65107543Sscottl break 66107543Sscottl fi 67107543Sscottl _uid=$(($_uid + 1)) 68107543Sscottl done 69107543Sscottl fi 70107543Sscottl echo $_nextuid 71107543Sscottl} 72107543Sscottl 73107543Sscottl# show_usage 74107543Sscottl# Display usage information for this utility. 75107543Sscottl# 76107543Sscottlshow_usage() { 77107543Sscottl echo "usage: ${THISCMD} [options]" 78107543Sscottl echo " options may include:" 79107543Sscottl echo " -C save to the configuration file only" 80127076Smtm echo " -D do not attempt to create the home directory" 81107543Sscottl echo " -E disable this account after creation" 82107543Sscottl echo " -G additional groups to add accounts to" 83107543Sscottl echo " -L login class of the user" 84107543Sscottl echo " -N do not read configuration file" 85127076Smtm echo " -S a nonexistent shell is not an error" 86107543Sscottl echo " -d home directory" 87107543Sscottl echo " -f file from which input will be received" 88112519Smtm echo " -g default login group" 89107543Sscottl echo " -h display this usage message" 90107543Sscottl echo " -k path to skeleton home directory" 91107543Sscottl echo " -m user welcome message file" 92107543Sscottl echo " -q absolute minimal user feedback" 93107543Sscottl echo " -s shell" 94107543Sscottl echo " -u uid to start at" 95107543Sscottl echo " -w password type: no, none, yes or random" 96107543Sscottl} 97107543Sscottl 98107543Sscottl# valid_shells 99107543Sscottl# Outputs a list of valid shells from /etc/shells. Only the 100107543Sscottl# basename of the shell is output. 101107543Sscottl# 102107543Sscottlvalid_shells() { 103107543Sscottl _prefix= 104107543Sscottl cat ${ETCSHELLS} | 105107543Sscottl while read _path _junk ; do 106107543Sscottl case $_path in 107107543Sscottl \#*|'') 108107543Sscottl ;; 109107543Sscottl *) 110107543Sscottl echo -n "${_prefix}`basename $_path`" 111107543Sscottl _prefix=' ' 112107543Sscottl ;; 113107543Sscottl esac 114107543Sscottl done 115116627Smtm 116127635Scperciva # /usr/sbin/nologin is a special case 117116627Smtm [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}" 118107543Sscottl} 119107543Sscottl 120107543Sscottl# fullpath_from_shell shell 121130160Smtm# Given $shell, which is either the full path to a shell or 122130160Smtm# the basename component of a valid shell, get the 123107543Sscottl# full path to the shell from the /etc/shells file. 124107543Sscottl# 125107543Sscottlfullpath_from_shell() { 126107543Sscottl _shell=$1 127107543Sscottl [ -z "$_shell" ] && return 1 128107543Sscottl 129135616Sroam # /usr/sbin/nologin is a special case; it needs to be handled 130135616Sroam # before the cat | while loop, since a 'return' from within 131135616Sroam # a subshell will not terminate the function's execution, and 132135616Sroam # the path to the nologin shell might be printed out twice. 133135616Sroam # 134135616Sroam if [ "$_shell" = "${NOLOGIN}" -o \ 135135616Sroam "$_shell" = "${NOLOGIN_PATH}" ]; then 136135616Sroam echo ${NOLOGIN_PATH} 137135616Sroam return 0; 138135616Sroam fi 139135616Sroam 140107543Sscottl cat ${ETCSHELLS} | 141107543Sscottl while read _path _junk ; do 142107543Sscottl case "$_path" in 143107543Sscottl \#*|'') 144107543Sscottl ;; 145107543Sscottl *) 146130160Smtm if [ "$_path" = "$_shell" -o \ 147130160Smtm "`basename $_path`" = "$_shell" ]; then 148107543Sscottl echo $_path 149107543Sscottl return 0 150107543Sscottl fi 151107543Sscottl ;; 152107543Sscottl esac 153107543Sscottl done 154116627Smtm 155107543Sscottl return 1 156107543Sscottl} 157107543Sscottl 158116627Smtm# shell_exists shell 159116627Smtm# If the given shell is listed in ${ETCSHELLS} or it is 160116627Smtm# the nologin shell this function will return 0. 161116627Smtm# Otherwise, it will return 1. If shell is valid but 162116627Smtm# the path is invalid or it is not executable it 163116627Smtm# will emit an informational message saying so. 164116627Smtm# 165116627Smtmshell_exists() 166116627Smtm{ 167116627Smtm _sh="$1" 168116627Smtm _shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1" 169116627Smtm 170116627Smtm if ! eval $_shellchk; then 171116627Smtm # The nologin shell is not listed in /etc/shells. 172116627Smtm if [ "$_sh" != "${NOLOGIN_PATH}" ]; then 173116627Smtm err "Invalid shell ($_sh) for user $username." 174116627Smtm return 1 175116627Smtm fi 176116627Smtm fi 177116627Smtm ! [ -x "$_sh" ] && 178116628Smtm info "The shell ($_sh) does not exist or is not executable." 179116627Smtm 180116627Smtm return 0 181116627Smtm} 182116627Smtm 183107543Sscottl# save_config 184107543Sscottl# Save some variables to a configuration file. 185107543Sscottl# Note: not all script variables are saved, only those that 186107543Sscottl# it makes sense to save. 187107543Sscottl# 188107543Sscottlsave_config() { 189107543Sscottl echo "# Configuration file for adduser(8)." > ${ADDUSERCONF} 190107543Sscottl echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF} 191109751Sfjoe echo "# Last Modified on `${DATECMD}`." >> ${ADDUSERCONF} 192107543Sscottl echo '' >> ${ADDUSERCONF} 193112433Smtm echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF} 194107543Sscottl echo "defaultclass=$uclass" >> ${ADDUSERCONF} 195107543Sscottl echo "defaultgroups=$ugroups" >> ${ADDUSERCONF} 196107543Sscottl echo "passwdtype=$passwdtype" >> ${ADDUSERCONF} 197107543Sscottl echo "homeprefix=$homeprefix" >> ${ADDUSERCONF} 198107543Sscottl echo "defaultshell=$ushell" >> ${ADDUSERCONF} 199107543Sscottl echo "udotdir=$udotdir" >> ${ADDUSERCONF} 200107543Sscottl echo "msgfile=$msgfile" >> ${ADDUSERCONF} 201107543Sscottl echo "disableflag=$disableflag" >> ${ADDUSERCONF} 202107543Sscottl} 203107543Sscottl 204107543Sscottl# add_user 205107543Sscottl# Add a user to the user database. If the user chose to send a welcome 206107543Sscottl# message or lock the account, do so. 207107543Sscottl# 208107543Sscottladd_user() { 209107543Sscottl 210107543Sscottl # Is this a configuration run? If so, don't modify user database. 211107543Sscottl # 212107543Sscottl if [ -n "$configflag" ]; then 213107543Sscottl save_config 214107543Sscottl return 215107543Sscottl fi 216107543Sscottl 217107543Sscottl _uid= 218107543Sscottl _name= 219107543Sscottl _comment= 220107543Sscottl _gecos= 221107543Sscottl _home= 222107543Sscottl _group= 223107543Sscottl _grouplist= 224107543Sscottl _shell= 225107543Sscottl _class= 226107543Sscottl _dotdir= 227107543Sscottl _expire= 228107543Sscottl _pwexpire= 229107543Sscottl _passwd= 230107543Sscottl _upasswd= 231107543Sscottl _passwdmethod= 232107543Sscottl 233109720Smtm _name="-n '$username'" 234110595Smtm [ -n "$uuid" ] && _uid='-u "$uuid"' 235110595Smtm [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"' 236110595Smtm [ -n "$ugroups" ] && _grouplist='-G "$ugroups"' 237110595Smtm [ -n "$ushell" ] && _shell='-s "$ushell"' 238110595Smtm [ -n "$uclass" ] && _class='-L "$uclass"' 239110595Smtm [ -n "$ugecos" ] && _comment='-c "$ugecos"' 240110595Smtm [ -n "$udotdir" ] && _dotdir='-k "$udotdir"' 241110595Smtm [ -n "$uexpire" ] && _expire='-e "$uexpire"' 242110595Smtm [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"' 243127076Smtm if [ -z "$Dflag" -a -n "$uhome" ]; then 244127076Smtm # The /nonexistent home directory is special. It 245127076Smtm # means the user has no home directory. 246127076Smtm if [ "$uhome" = "$NOHOME" ]; then 247127076Smtm _home='-d "$uhome"' 248127076Smtm else 249127076Smtm _home='-m -d "$uhome"' 250127076Smtm fi 251127076Smtm elif [ -n "$Dflag" -a -n "$uhome" ]; then 252127076Smtm _home='-d "$uhome"' 253127076Smtm fi 254107543Sscottl case $passwdtype in 255107543Sscottl no) 256107543Sscottl _passwdmethod="-w no" 257107543Sscottl _passwd="-h -" 258107543Sscottl ;; 259107543Sscottl yes) 260110595Smtm # Note on processing the password: The outer double quotes 261110595Smtm # make literal everything except ` and \ and $. 262110595Smtm # The outer single quotes make literal ` and $. 263110595Smtm # We can ensure the \ isn't treated specially by specifying 264110595Smtm # the -r switch to the read command used to obtain the input. 265110595Smtm # 266107543Sscottl _passwdmethod="-w yes" 267107543Sscottl _passwd="-h 0" 268110595Smtm _upasswd='echo "$upass" |' 269107543Sscottl ;; 270107543Sscottl none) 271107543Sscottl _passwdmethod="-w none" 272107543Sscottl ;; 273107543Sscottl random) 274107543Sscottl _passwdmethod="-w random" 275107543Sscottl ;; 276107543Sscottl esac 277107543Sscottl 278107543Sscottl _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment" 279107543Sscottl _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd" 280107543Sscottl _pwcmd="$_pwcmd $_expire $_pwexpire" 281107543Sscottl 282107543Sscottl if ! _output=`eval $_pwcmd` ; then 283107543Sscottl err "There was an error adding user ($username)." 284107543Sscottl return 1 285107543Sscottl else 286107543Sscottl info "Successfully added ($username) to the user database." 287107543Sscottl if [ "random" = "$passwdtype" ]; then 288107543Sscottl randompass="$_output" 289107543Sscottl info "Password for ($username) is: $randompass" 290107543Sscottl fi 291107543Sscottl fi 292107543Sscottl 293107543Sscottl if [ -n "$disableflag" ]; then 294107543Sscottl if ${PWCMD} lock $username ; then 295107543Sscottl info "Account ($username) is locked." 296107543Sscottl else 297107543Sscottl info "Account ($username) could NOT be locked." 298107543Sscottl fi 299107543Sscottl fi 300107543Sscottl 301107543Sscottl _line= 302107543Sscottl _owner= 303107543Sscottl _perms= 304107543Sscottl if [ -n "$msgflag" ]; then 305107543Sscottl [ -r "$msgfile" ] && { 306107543Sscottl # We're evaluating the contents of an external file. 307107543Sscottl # Let's not open ourselves up for attack. _perms will 308107543Sscottl # be empty if it's writeable only by the owner. _owner 309107543Sscottl # will *NOT* be empty if the file is owned by root. 310107543Sscottl # 311107543Sscottl _dir="`dirname $msgfile`" 312107543Sscottl _file="`basename $msgfile`" 313107543Sscottl _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune` 314107543Sscottl _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune` 315107543Sscottl if [ -z "$_owner" -o -n "$_perms" ]; then 316107543Sscottl err "The message file ($msgfile) may be writeable only by root." 317107543Sscottl return 1 318107543Sscottl fi 319107543Sscottl cat "$msgfile" | 320107543Sscottl while read _line ; do 321107543Sscottl eval echo "$_line" 322107543Sscottl done | ${MAILCMD} -s"Welcome" ${username} 323107543Sscottl info "Sent welcome message to ($username)." 324107543Sscottl } 325107543Sscottl fi 326107543Sscottl} 327107543Sscottl 328107543Sscottl# get_user 329107543Sscottl# Reads username of the account from standard input or from a global 330107543Sscottl# variable containing an account line from a file. The username is 331107543Sscottl# required. If this is an interactive session it will prompt in 332107543Sscottl# a loop until a username is entered. If it is batch processing from 333107543Sscottl# a file it will output an error message and return to the caller. 334107543Sscottl# 335107543Sscottlget_user() { 336107543Sscottl _input= 337107543Sscottl 338107543Sscottl # No need to take down user names if this is a configuration saving run. 339107543Sscottl [ -n "$configflag" ] && return 340107543Sscottl 341107543Sscottl while : ; do 342107543Sscottl if [ -z "$fflag" ]; then 343107543Sscottl echo -n "Username: " 344107543Sscottl read _input 345107543Sscottl else 346107543Sscottl _input="`echo "$fileline" | cut -f1 -d:`" 347107543Sscottl fi 348107543Sscottl 349167917Sle # There *must* be a username, and it must not exist. If 350167917Sle # this is an interactive session give the user an 351167917Sle # opportunity to retry. 352107543Sscottl # 353107543Sscottl if [ -z "$_input" ]; then 354107543Sscottl err "You must enter a username!" 355107543Sscottl [ -z "$fflag" ] && continue 356107543Sscottl fi 357167917Sle ${PWCMD} usershow $_input > /dev/null 2>&1 358167917Sle if [ "$?" -eq 0 ]; then 359167917Sle err "User exists!" 360167917Sle [ -z "$fflag" ] && continue 361167917Sle fi 362107543Sscottl break 363107543Sscottl done 364107543Sscottl username="$_input" 365107543Sscottl} 366107543Sscottl 367107543Sscottl# get_gecos 368107543Sscottl# Reads extra information about the user. Can be used both in interactive 369107543Sscottl# and batch (from file) mode. 370107543Sscottl# 371107543Sscottlget_gecos() { 372107543Sscottl _input= 373107543Sscottl 374107543Sscottl # No need to take down additional user information for a configuration run. 375107543Sscottl [ -n "$configflag" ] && return 376107543Sscottl 377107543Sscottl if [ -z "$fflag" ]; then 378107543Sscottl echo -n "Full name: " 379107543Sscottl read _input 380107543Sscottl else 381107543Sscottl _input="`echo "$fileline" | cut -f7 -d:`" 382107543Sscottl fi 383107543Sscottl ugecos="$_input" 384107543Sscottl} 385107543Sscottl 386107543Sscottl# get_shell 387107543Sscottl# Get the account's shell. Works in interactive and batch mode. It 388130160Smtm# accepts either the base name of the shell or the full path. 389107543Sscottl# If an invalid shell is entered it will simply use the default shell. 390107543Sscottl# 391107543Sscottlget_shell() { 392107543Sscottl _input= 393107543Sscottl _fullpath= 394107543Sscottl ushell="$defaultshell" 395107543Sscottl 396107543Sscottl # Make sure the current value of the shell is a valid one 397127076Smtm if [ -z "$Sflag" ]; then 398127076Smtm if ! shell_exists $ushell ; then 399127076Smtm info "Using default shell ${defaultshell}." 400127076Smtm ushell="$defaultshell" 401127076Smtm fi 402116627Smtm fi 403107543Sscottl 404107543Sscottl if [ -z "$fflag" ]; then 405107543Sscottl echo -n "Shell ($shells) [`basename $ushell`]: " 406107543Sscottl read _input 407107543Sscottl else 408107543Sscottl _input="`echo "$fileline" | cut -f9 -d:`" 409107543Sscottl fi 410107543Sscottl if [ -n "$_input" ]; then 411127076Smtm if [ -n "$Sflag" ]; then 412127076Smtm ushell="$_input" 413107543Sscottl else 414127076Smtm _fullpath=`fullpath_from_shell $_input` 415127076Smtm if [ -n "$_fullpath" ]; then 416127076Smtm ushell="$_fullpath" 417127076Smtm else 418127076Smtm err "Invalid shell ($_input) for user $username." 419127076Smtm info "Using default shell ${defaultshell}." 420127076Smtm ushell="$defaultshell" 421127076Smtm fi 422107543Sscottl fi 423107543Sscottl fi 424107543Sscottl} 425107543Sscottl 426107543Sscottl# get_homedir 427107543Sscottl# Reads the account's home directory. Used both with interactive input 428107543Sscottl# and batch input. 429107543Sscottl# 430107543Sscottlget_homedir() { 431107543Sscottl _input= 432107543Sscottl if [ -z "$fflag" ]; then 433107543Sscottl echo -n "Home directory [${homeprefix}/${username}]: " 434107543Sscottl read _input 435107543Sscottl else 436107543Sscottl _input="`echo "$fileline" | cut -f8 -d:`" 437107543Sscottl fi 438107543Sscottl 439107543Sscottl if [ -n "$_input" ]; then 440107543Sscottl uhome="$_input" 441107543Sscottl # if this is a configuration run, then user input is the home 442107543Sscottl # directory prefix. Otherwise it is understood to 443107543Sscottl # be $prefix/$user 444107543Sscottl # 445107543Sscottl [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome" 446107543Sscottl else 447107543Sscottl uhome="${homeprefix}/${username}" 448107543Sscottl fi 449107543Sscottl} 450107543Sscottl 451107543Sscottl# get_uid 452107543Sscottl# Reads a numeric userid in an interactive or batch session. Automatically 453107543Sscottl# allocates one if it is not specified. 454107543Sscottl# 455107543Sscottlget_uid() { 456154688Smatteo if [ -z "$uuid" ]; then 457154688Smatteo uuid=${uidstart} 458154688Smatteo fi 459154688Smatteo 460107543Sscottl _input= 461107543Sscottl _prompt= 462107543Sscottl 463107543Sscottl # No need to take down uids for a configuration saving run. 464107543Sscottl [ -n "$configflag" ] && return 465107543Sscottl 466107543Sscottl if [ -n "$uuid" ]; then 467107543Sscottl _prompt="Uid [$uuid]: " 468107543Sscottl else 469107543Sscottl _prompt="Uid (Leave empty for default): " 470107543Sscottl fi 471107543Sscottl if [ -z "$fflag" ]; then 472109573Sfjoe echo -n "$_prompt" 473107543Sscottl read _input 474107543Sscottl else 475107543Sscottl _input="`echo "$fileline" | cut -f2 -d:`" 476107543Sscottl fi 477107543Sscottl 478107543Sscottl [ -n "$_input" ] && uuid=$_input 479107543Sscottl uuid=`get_nextuid $uuid` 480107543Sscottl uidstart=$uuid 481107543Sscottl} 482107543Sscottl 483107543Sscottl# get_class 484107543Sscottl# Reads login class of account. Can be used in interactive or batch mode. 485107543Sscottl# 486107543Sscottlget_class() { 487107543Sscottl uclass="$defaultclass" 488107543Sscottl _input= 489107543Sscottl _class=${uclass:-"default"} 490107543Sscottl 491107543Sscottl if [ -z "$fflag" ]; then 492107543Sscottl echo -n "Login class [$_class]: " 493107543Sscottl read _input 494107543Sscottl else 495107543Sscottl _input="`echo "$fileline" | cut -f4 -d:`" 496107543Sscottl fi 497107543Sscottl 498107543Sscottl [ -n "$_input" ] && uclass="$_input" 499107543Sscottl} 500107543Sscottl 501107543Sscottl# get_logingroup 502107543Sscottl# Reads user's login group. Can be used in both interactive and batch 503107543Sscottl# modes. The specified value can be a group name or its numeric id. 504112433Smtm# This routine leaves the field blank if nothing is provided and 505112433Smtm# a default login group has not been set. The pw(8) command 506112433Smtm# will then provide a login group with the same name as the username. 507107543Sscottl# 508107543Sscottlget_logingroup() { 509112433Smtm ulogingroup="$defaultLgroup" 510107543Sscottl _input= 511107543Sscottl 512107543Sscottl if [ -z "$fflag" ]; then 513112433Smtm echo -n "Login group [${ulogingroup:-$username}]: " 514107543Sscottl read _input 515107543Sscottl else 516107543Sscottl _input="`echo "$fileline" | cut -f3 -d:`" 517107543Sscottl fi 518107543Sscottl 519107543Sscottl # Pw(8) will use the username as login group if it's left empty 520112433Smtm [ -n "$_input" ] && ulogingroup="$_input" 521107543Sscottl} 522107543Sscottl 523107543Sscottl# get_groups 524107543Sscottl# Read additional groups for the user. It can be used in both interactive 525107543Sscottl# and batch modes. 526107543Sscottl# 527107543Sscottlget_groups() { 528107543Sscottl ugroups="$defaultgroups" 529107543Sscottl _input= 530107543Sscottl _group=${ulogingroup:-"${username}"} 531107543Sscottl 532107543Sscottl if [ -z "$configflag" ]; then 533107543Sscottl [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username" 534107543Sscottl [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: " 535107543Sscottl else 536107543Sscottl [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: " 537107543Sscottl fi 538107543Sscottl read _input 539107543Sscottl 540107543Sscottl [ -n "$_input" ] && ugroups="$_input" 541107543Sscottl} 542107543Sscottl 543107543Sscottl# get_expire_dates 544107543Sscottl# Read expiry information for the account and also for the password. This 545107543Sscottl# routine is used only from batch processing mode. 546107543Sscottl# 547107543Sscottlget_expire_dates() { 548107543Sscottl upwexpire="`echo "$fileline" | cut -f5 -d:`" 549107543Sscottl uexpire="`echo "$fileline" | cut -f6 -d:`" 550107543Sscottl} 551107543Sscottl 552107543Sscottl# get_password 553107543Sscottl# Read the password in batch processing mode. The password field matters 554107543Sscottl# only when the password type is "yes" or "random". If the field is empty and the 555107543Sscottl# password type is "yes", then it assumes the account has an empty passsword 556107543Sscottl# and changes the password type accordingly. If the password type is "random" 557107543Sscottl# and the password field is NOT empty, then it assumes the account will NOT 558107543Sscottl# have a random password and set passwdtype to "yes." 559107543Sscottl# 560107543Sscottlget_password() { 561107543Sscottl # We may temporarily change a password type. Make sure it's changed 562107543Sscottl # back to whatever it was before we process the next account. 563107543Sscottl # 564107543Sscottl [ -n "$savedpwtype" ] && { 565107543Sscottl passwdtype=$savedpwtype 566107543Sscottl savedpwtype= 567107543Sscottl } 568107543Sscottl 569107543Sscottl # There may be a ':' in the password 570107543Sscottl upass=${fileline#*:*:*:*:*:*:*:*:*:} 571107543Sscottl 572107543Sscottl if [ -z "$upass" ]; then 573107543Sscottl case $passwdtype in 574107543Sscottl yes) 575107543Sscottl # if it's empty, assume an empty password 576107543Sscottl passwdtype=none 577107543Sscottl savedpwtype=yes 578107543Sscottl ;; 579107543Sscottl esac 580107543Sscottl else 581107543Sscottl case $passwdtype in 582107543Sscottl random) 583107543Sscottl passwdtype=yes 584107543Sscottl savedpwtype=random 585107543Sscottl ;; 586107543Sscottl esac 587107543Sscottl fi 588107543Sscottl} 589107543Sscottl 590107543Sscottl# input_from_file 591107543Sscottl# Reads a line of account information from standard input and 592107543Sscottl# adds it to the user database. 593107543Sscottl# 594107543Sscottlinput_from_file() { 595107543Sscottl _field= 596107543Sscottl 597110595Smtm while read -r fileline ; do 598107543Sscottl case "$fileline" in 599107543Sscottl \#*|'') 600107543Sscottl ;; 601107543Sscottl esac 602107543Sscottl 603107543Sscottl get_user || continue 604107543Sscottl get_gecos 605107543Sscottl get_uid 606107543Sscottl get_logingroup 607107543Sscottl get_class 608107543Sscottl get_shell 609107543Sscottl get_homedir 610107543Sscottl get_password 611107543Sscottl get_expire_dates 612107543Sscottl 613107543Sscottl add_user 614107543Sscottl done 615107543Sscottl} 616107543Sscottl 617107543Sscottl# input_interactive 618107543Sscottl# Prompts for user information interactively, and commits to 619107543Sscottl# the user database. 620107543Sscottl# 621107543Sscottlinput_interactive() { 622107543Sscottl 623107543Sscottl _disable= 624107543Sscottl _pass= 625107543Sscottl _passconfirm= 626107543Sscottl _random="no" 627107543Sscottl _emptypass="no" 628107543Sscottl _usepass="yes" 629112401Smtm _logingroup_ok="no" 630112401Smtm _groups_ok="no" 631107543Sscottl case $passwdtype in 632107543Sscottl none) 633107543Sscottl _emptypass="yes" 634107543Sscottl _usepass="yes" 635107543Sscottl ;; 636107543Sscottl no) 637107543Sscottl _usepass="no" 638107543Sscottl ;; 639107543Sscottl random) 640107543Sscottl _random="yes" 641107543Sscottl ;; 642107543Sscottl esac 643107543Sscottl 644107543Sscottl get_user 645107543Sscottl get_gecos 646107543Sscottl get_uid 647110537Sadrian 648110537Sadrian # The case where group = user is handled elsewhere, so 649110537Sadrian # validate any other groups the user is invited to. 650110537Sadrian until [ "$_logingroup_ok" = yes ]; do 651110537Sadrian get_logingroup 652110537Sadrian _logingroup_ok=yes 653110537Sadrian if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then 654110537Sadrian if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then 655110537Sadrian echo "Group $ulogingroup does not exist!" 656110537Sadrian _logingroup_ok=no 657110537Sadrian fi 658110537Sadrian fi 659110537Sadrian done 660110537Sadrian until [ "$_groups_ok" = yes ]; do 661110537Sadrian get_groups 662110537Sadrian _groups_ok=yes 663110537Sadrian for i in $ugroups; do 664110537Sadrian if [ "$username" != "$i" ]; then 665110537Sadrian if ! ${PWCMD} show group $i > /dev/null 2>&1; then 666110537Sadrian echo "Group $i does not exist!" 667110537Sadrian _groups_ok=no 668110537Sadrian fi 669110537Sadrian fi 670110537Sadrian done 671110537Sadrian done 672110537Sadrian 673107543Sscottl get_class 674107543Sscottl get_shell 675107543Sscottl get_homedir 676107543Sscottl 677107543Sscottl while : ; do 678107543Sscottl echo -n "Use password-based authentication? [$_usepass]: " 679107543Sscottl read _input 680107543Sscottl [ -z "$_input" ] && _input=$_usepass 681107543Sscottl case $_input in 682107543Sscottl [Nn][Oo]|[Nn]) 683107543Sscottl passwdtype="no" 684107543Sscottl ;; 685107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 686107543Sscottl while : ; do 687107543Sscottl echo -n "Use an empty password? (yes/no) [$_emptypass]: " 688107543Sscottl read _input 689107543Sscottl [ -n "$_input" ] && _emptypass=$_input 690107543Sscottl case $_emptypass in 691107543Sscottl [Nn][Oo]|[Nn]) 692107543Sscottl echo -n "Use a random password? (yes/no) [$_random]: " 693107543Sscottl read _input 694107543Sscottl [ -n "$_input" ] && _random="$_input" 695107543Sscottl case $_random in 696107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 697107543Sscottl passwdtype="random" 698107543Sscottl break 699107543Sscottl ;; 700107543Sscottl esac 701107543Sscottl passwdtype="yes" 702112401Smtm [ -n "$configflag" ] && break 703107543Sscottl trap 'stty echo; exit' 0 1 2 3 15 704107543Sscottl stty -echo 705107543Sscottl echo -n "Enter password: " 706110595Smtm read -r upass 707107543Sscottl echo'' 708107543Sscottl echo -n "Enter password again: " 709116623Smtm read -r _passconfirm 710107543Sscottl echo '' 711107543Sscottl stty echo 712107543Sscottl # if user entered a blank password 713107543Sscottl # explicitly ask again. 714107543Sscottl [ -z "$upass" -a -z "$_passconfirm" ] \ 715107543Sscottl && continue 716107543Sscottl ;; 717107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 718107543Sscottl passwdtype="none" 719107543Sscottl break; 720107543Sscottl ;; 721107543Sscottl *) 722107543Sscottl # invalid answer; repeat the loop 723107543Sscottl continue 724107543Sscottl ;; 725107543Sscottl esac 726107543Sscottl if [ "$upass" != "$_passconfirm" ]; then 727107543Sscottl echo "Passwords did not match!" 728107543Sscottl continue 729107543Sscottl fi 730107543Sscottl break 731107543Sscottl done 732107543Sscottl ;; 733107543Sscottl *) 734107543Sscottl # invalid answer; repeat loop 735107543Sscottl continue 736107543Sscottl ;; 737107543Sscottl esac 738107543Sscottl break; 739107543Sscottl done 740107543Sscottl _disable=${disableflag:-"no"} 741107543Sscottl while : ; do 742107543Sscottl echo -n "Lock out the account after creation? [$_disable]: " 743107543Sscottl read _input 744107543Sscottl [ -z "$_input" ] && _input=$_disable 745107543Sscottl case $_input in 746107543Sscottl [Nn][Oo]|[Nn]) 747107543Sscottl disableflag= 748107543Sscottl ;; 749107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 750107543Sscottl disableflag=yes 751107543Sscottl ;; 752107543Sscottl *) 753107543Sscottl # invalid answer; repeat loop 754107543Sscottl continue 755107543Sscottl ;; 756107543Sscottl esac 757107543Sscottl break 758107543Sscottl done 759107543Sscottl 760107543Sscottl # Display the information we have so far and prompt to 761107543Sscottl # commit it. 762107543Sscottl # 763107543Sscottl _disable=${disableflag:-"no"} 764107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username 765107543Sscottl case $passwdtype in 766107543Sscottl yes) 767107543Sscottl _pass='*****' 768107543Sscottl ;; 769107543Sscottl no) 770107543Sscottl _pass='<disabled>' 771107543Sscottl ;; 772107543Sscottl none) 773107543Sscottl _pass='<blank>' 774107543Sscottl ;; 775107543Sscottl random) 776107543Sscottl _pass='<random>' 777107543Sscottl ;; 778107543Sscottl esac 779109751Sfjoe [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass" 780109751Sfjoe [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype" 781107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos" 782107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid" 783107543Sscottl printf "%-10s : %s\n" "Class" "$uclass" 784112433Smtm printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups" 785107543Sscottl printf "%-10s : %s\n" "Home" "$uhome" 786107543Sscottl printf "%-10s : %s\n" "Shell" "$ushell" 787107543Sscottl printf "%-10s : %s\n" "Locked" "$_disable" 788107543Sscottl while : ; do 789107543Sscottl echo -n "OK? (yes/no): " 790107543Sscottl read _input 791107543Sscottl case $_input in 792107543Sscottl [Nn][Oo]|[Nn]) 793107543Sscottl return 1 794107543Sscottl ;; 795107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 796107543Sscottl add_user 797107543Sscottl ;; 798107543Sscottl *) 799107543Sscottl continue 800107543Sscottl ;; 801107543Sscottl esac 802107543Sscottl break 803107543Sscottl done 804107543Sscottl return 0 805107543Sscottl} 806107543Sscottl 807145618Srobert#### END SUBROUTINE DEFINITION #### 808107543Sscottl 809107543SscottlTHISCMD=`/usr/bin/basename $0` 810107543SscottlDEFAULTSHELL=/bin/sh 811107543SscottlADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}" 812107543SscottlPWCMD="${PWCMD:-/usr/sbin/pw}" 813107543SscottlMAILCMD="${MAILCMD:-mail}" 814107543SscottlETCSHELLS="${ETCSHELLS:-/etc/shells}" 815127076SmtmNOHOME="/nonexistent" 816116627SmtmNOLOGIN="nologin" 817127635ScpercivaNOLOGIN_PATH="/usr/sbin/nologin" 818109751SfjoeGREPCMD="/usr/bin/grep" 819109751SfjoeDATECMD="/bin/date" 820107543Sscottl 821107543Sscottl# Set default values 822107543Sscottl# 823107543Sscottlusername= 824107543Sscottluuid= 825107543Sscottluidstart= 826107543Sscottlugecos= 827107543Sscottlulogingroup= 828107543Sscottluclass= 829107543Sscottluhome= 830107543Sscottlupass= 831107543Sscottlushell= 832107543Sscottludotdir=/usr/share/skel 833107543Sscottlugroups= 834107543Sscottluexpire= 835107543Sscottlupwexpire= 836107543Sscottlshells="`valid_shells`" 837107543Sscottlpasswdtype="yes" 838107543Sscottlmsgfile=/etc/adduser.msg 839107543Sscottlmsgflag= 840107543Sscottlquietflag= 841107543Sscottlconfigflag= 842107543Sscottlfflag= 843107543Sscottlinfile= 844107543Sscottldisableflag= 845127076SmtmDflag= 846127076SmtmSflag= 847107543Sscottlreadconfig="yes" 848107543Sscottlhomeprefix="/home" 849107543Sscottlrandompass= 850107543Sscottlfileline= 851107543Sscottlsavedpwtype= 852107543Sscottldefaultclass= 853112433SmtmdefaultLgroup= 854116784Smtmdefaultgroups= 855107543Sscottldefaultshell="${DEFAULTSHELL}" 856107543Sscottl 857107543Sscottl# Make sure the user running this program is root. This isn't a security 858107543Sscottl# measure as much as it is a usefull method of reminding the user to 859107543Sscottl# 'su -' before he/she wastes time entering data that won't be saved. 860107543Sscottl# 861107543Sscottlprocowner=${procowner:-`/usr/bin/id -u`} 862107543Sscottlif [ "$procowner" != "0" ]; then 863107543Sscottl err 'you must be the super-user (uid 0) to use this utility.' 864107543Sscottl exit 1 865107543Sscottlfi 866107543Sscottl 867107543Sscottl# Overide from our conf file 868107543Sscottl# Quickly go through the commandline line to see if we should read 869107543Sscottl# from our configuration file. The actual parsing of the commandline 870107543Sscottl# arguments happens after we read in our configuration file (commandline 871107543Sscottl# should override configuration file). 872107543Sscottl# 873107543Sscottlfor _i in $* ; do 874107543Sscottl if [ "$_i" = "-N" ]; then 875107543Sscottl readconfig= 876107543Sscottl break; 877107543Sscottl fi 878107543Sscottldone 879107543Sscottlif [ -n "$readconfig" ]; then 880107543Sscottl # On a long-lived system, the first time this script is run it 881107543Sscottl # will barf upon reading the configuration file for its perl predecessor. 882107543Sscottl if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then 883107543Sscottl [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1 884107543Sscottl fi 885107543Sscottlfi 886107543Sscottl 887107543Sscottl# Proccess command-line options 888107543Sscottl# 889107543Sscottlfor _switch ; do 890107543Sscottl case $_switch in 891107543Sscottl -L) 892107543Sscottl defaultclass="$2" 893107543Sscottl shift; shift 894107543Sscottl ;; 895107543Sscottl -C) 896107543Sscottl configflag=yes 897107543Sscottl shift 898107543Sscottl ;; 899127076Smtm -D) 900127076Smtm Dflag=yes 901127076Smtm shift 902127076Smtm ;; 903107543Sscottl -E) 904107543Sscottl disableflag=yes 905107543Sscottl shift 906107543Sscottl ;; 907107543Sscottl -k) 908107543Sscottl udotdir="$2" 909107543Sscottl shift; shift 910107543Sscottl ;; 911107543Sscottl -f) 912107543Sscottl [ "$2" != "-" ] && infile="$2" 913107543Sscottl fflag=yes 914107543Sscottl shift; shift 915107543Sscottl ;; 916112433Smtm -g) 917112433Smtm defaultLgroup="$2" 918112433Smtm shift; shift 919112433Smtm ;; 920107543Sscottl -G) 921107543Sscottl defaultgroups="$2" 922107543Sscottl shift; shift 923107543Sscottl ;; 924107543Sscottl -h) 925107543Sscottl show_usage 926107543Sscottl exit 0 927107543Sscottl ;; 928107543Sscottl -d) 929107543Sscottl homeprefix="$2" 930107543Sscottl shift; shift 931107543Sscottl ;; 932107543Sscottl -m) 933107543Sscottl case "$2" in 934107543Sscottl [Nn][Oo]) 935107543Sscottl msgflag= 936107543Sscottl ;; 937107543Sscottl *) 938107543Sscottl msgflag=yes 939107543Sscottl msgfile="$2" 940107543Sscottl ;; 941107543Sscottl esac 942107543Sscottl shift; shift 943107543Sscottl ;; 944107543Sscottl -N) 945107543Sscottl readconfig= 946107543Sscottl shift 947107543Sscottl ;; 948107543Sscottl -w) 949107543Sscottl case "$2" in 950107543Sscottl no|none|random|yes) 951107543Sscottl passwdtype=$2 952107543Sscottl ;; 953107543Sscottl *) 954107543Sscottl show_usage 955107543Sscottl exit 1 956107543Sscottl ;; 957107543Sscottl esac 958107543Sscottl shift; shift 959107543Sscottl ;; 960107543Sscottl -q) 961107543Sscottl quietflag=yes 962107543Sscottl shift 963107543Sscottl ;; 964107543Sscottl -s) 965107543Sscottl defaultshell="`fullpath_from_shell $2`" 966107543Sscottl shift; shift 967107543Sscottl ;; 968127076Smtm -S) 969127076Smtm Sflag=yes 970127076Smtm shift 971127076Smtm ;; 972107543Sscottl -u) 973107543Sscottl uidstart=$2 974107543Sscottl shift; shift 975107543Sscottl ;; 976107543Sscottl esac 977107543Sscottldone 978107543Sscottl 979107543Sscottl# If the -f switch was used, get input from a file. Otherwise, 980107543Sscottl# this is an interactive session. 981107543Sscottl# 982107543Sscottlif [ -n "$fflag" ]; then 983107543Sscottl if [ -z "$infile" ]; then 984107543Sscottl input_from_file 985107543Sscottl elif [ -n "$infile" ]; then 986107543Sscottl if [ -r "$infile" ]; then 987107543Sscottl input_from_file < $infile 988107543Sscottl else 989107543Sscottl err "File ($infile) is unreadable or does not exist." 990107543Sscottl fi 991107543Sscottl fi 992107543Sscottlelse 993107543Sscottl input_interactive 994109768Smtm while : ; do 995112401Smtm if [ -z "$configflag" ]; then 996112401Smtm echo -n "Add another user? (yes/no): " 997112401Smtm else 998112401Smtm echo -n "Re-edit the default configuration? (yes/no): " 999112401Smtm fi 1000109768Smtm read _input 1001109768Smtm case $_input in 1002109768Smtm [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 1003109768Smtm uidstart=`get_nextuid $uidstart` 1004109768Smtm input_interactive 1005109768Smtm continue 1006109768Smtm ;; 1007109768Smtm [Nn][Oo]|[Nn]) 1008109768Smtm echo "Goodbye!" 1009109768Smtm ;; 1010109768Smtm *) 1011109768Smtm continue 1012109768Smtm ;; 1013109768Smtm esac 1014109768Smtm break 1015109768Smtm done 1016107543Sscottlfi 1017