adduser.sh revision 232146
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 232146 2012-02-25 07:58:59Z deischen $ 28107543Sscottl# 29107543Sscottl 30107543Sscottl# err msg 31202312Sjoel# Display $msg on stderr, unless we're being quiet. 32202312Sjoel# 33107543Sscottlerr() { 34107543Sscottl if [ -z "$quietflag" ]; then 35202312Sjoel echo 1>&2 ${THISCMD}: ERROR: $* 36107543Sscottl fi 37107543Sscottl} 38107543Sscottl 39107543Sscottl# info msg 40202312Sjoel# Display $msg on stdout, unless we're being quiet. 41202312Sjoel# 42107543Sscottlinfo() { 43107543Sscottl if [ -z "$quietflag" ]; then 44202312Sjoel 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). 53202312Sjoel# 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" 84175719Smtm echo " -M file permission for home directory" 85107543Sscottl echo " -N do not read configuration file" 86127076Smtm echo " -S a nonexistent shell is not an error" 87107543Sscottl echo " -d home directory" 88107543Sscottl echo " -f file from which input will be received" 89112519Smtm echo " -g default login group" 90107543Sscottl echo " -h display this usage message" 91107543Sscottl echo " -k path to skeleton home directory" 92107543Sscottl echo " -m user welcome message file" 93107543Sscottl echo " -q absolute minimal user feedback" 94107543Sscottl echo " -s shell" 95107543Sscottl echo " -u uid to start at" 96107543Sscottl echo " -w password type: no, none, yes or random" 97107543Sscottl} 98107543Sscottl 99107543Sscottl# valid_shells 100107543Sscottl# Outputs a list of valid shells from /etc/shells. Only the 101107543Sscottl# basename of the shell is output. 102107543Sscottl# 103107543Sscottlvalid_shells() { 104107543Sscottl _prefix= 105107543Sscottl cat ${ETCSHELLS} | 106107543Sscottl while read _path _junk ; do 107107543Sscottl case $_path in 108107543Sscottl \#*|'') 109107543Sscottl ;; 110107543Sscottl *) 111107543Sscottl echo -n "${_prefix}`basename $_path`" 112107543Sscottl _prefix=' ' 113107543Sscottl ;; 114107543Sscottl esac 115107543Sscottl done 116116627Smtm 117127635Scperciva # /usr/sbin/nologin is a special case 118116627Smtm [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}" 119107543Sscottl} 120107543Sscottl 121107543Sscottl# fullpath_from_shell shell 122130160Smtm# Given $shell, which is either the full path to a shell or 123130160Smtm# the basename component of a valid shell, get the 124107543Sscottl# full path to the shell from the /etc/shells file. 125107543Sscottl# 126107543Sscottlfullpath_from_shell() { 127107543Sscottl _shell=$1 128107543Sscottl [ -z "$_shell" ] && return 1 129107543Sscottl 130135616Sroam # /usr/sbin/nologin is a special case; it needs to be handled 131135616Sroam # before the cat | while loop, since a 'return' from within 132135616Sroam # a subshell will not terminate the function's execution, and 133135616Sroam # the path to the nologin shell might be printed out twice. 134135616Sroam # 135135616Sroam if [ "$_shell" = "${NOLOGIN}" -o \ 136135616Sroam "$_shell" = "${NOLOGIN_PATH}" ]; then 137135616Sroam echo ${NOLOGIN_PATH} 138135616Sroam return 0; 139135616Sroam fi 140135616Sroam 141107543Sscottl cat ${ETCSHELLS} | 142107543Sscottl while read _path _junk ; do 143107543Sscottl case "$_path" in 144107543Sscottl \#*|'') 145107543Sscottl ;; 146107543Sscottl *) 147130160Smtm if [ "$_path" = "$_shell" -o \ 148130160Smtm "`basename $_path`" = "$_shell" ]; then 149107543Sscottl echo $_path 150107543Sscottl return 0 151107543Sscottl fi 152107543Sscottl ;; 153107543Sscottl esac 154107543Sscottl done 155116627Smtm 156107543Sscottl return 1 157107543Sscottl} 158107543Sscottl 159116627Smtm# shell_exists shell 160116627Smtm# If the given shell is listed in ${ETCSHELLS} or it is 161116627Smtm# the nologin shell this function will return 0. 162116627Smtm# Otherwise, it will return 1. If shell is valid but 163116627Smtm# the path is invalid or it is not executable it 164116627Smtm# will emit an informational message saying so. 165116627Smtm# 166202312Sjoelshell_exists() { 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} 193175719Smtm echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF} 194112433Smtm echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF} 195107543Sscottl echo "defaultclass=$uclass" >> ${ADDUSERCONF} 196107543Sscottl echo "defaultgroups=$ugroups" >> ${ADDUSERCONF} 197107543Sscottl echo "passwdtype=$passwdtype" >> ${ADDUSERCONF} 198107543Sscottl echo "homeprefix=$homeprefix" >> ${ADDUSERCONF} 199107543Sscottl echo "defaultshell=$ushell" >> ${ADDUSERCONF} 200107543Sscottl echo "udotdir=$udotdir" >> ${ADDUSERCONF} 201107543Sscottl echo "msgfile=$msgfile" >> ${ADDUSERCONF} 202107543Sscottl echo "disableflag=$disableflag" >> ${ADDUSERCONF} 203168656Smtm echo "uidstart=$uidstart" >> ${ADDUSERCONF} 204107543Sscottl} 205107543Sscottl 206107543Sscottl# add_user 207107543Sscottl# Add a user to the user database. If the user chose to send a welcome 208107543Sscottl# message or lock the account, do so. 209107543Sscottl# 210107543Sscottladd_user() { 211107543Sscottl 212107543Sscottl # Is this a configuration run? If so, don't modify user database. 213107543Sscottl # 214107543Sscottl if [ -n "$configflag" ]; then 215107543Sscottl save_config 216107543Sscottl return 217107543Sscottl fi 218107543Sscottl 219107543Sscottl _uid= 220107543Sscottl _name= 221107543Sscottl _comment= 222107543Sscottl _gecos= 223107543Sscottl _home= 224107543Sscottl _group= 225107543Sscottl _grouplist= 226107543Sscottl _shell= 227107543Sscottl _class= 228107543Sscottl _dotdir= 229107543Sscottl _expire= 230107543Sscottl _pwexpire= 231107543Sscottl _passwd= 232107543Sscottl _upasswd= 233107543Sscottl _passwdmethod= 234107543Sscottl 235109720Smtm _name="-n '$username'" 236110595Smtm [ -n "$uuid" ] && _uid='-u "$uuid"' 237110595Smtm [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"' 238110595Smtm [ -n "$ugroups" ] && _grouplist='-G "$ugroups"' 239110595Smtm [ -n "$ushell" ] && _shell='-s "$ushell"' 240110595Smtm [ -n "$uclass" ] && _class='-L "$uclass"' 241110595Smtm [ -n "$ugecos" ] && _comment='-c "$ugecos"' 242110595Smtm [ -n "$udotdir" ] && _dotdir='-k "$udotdir"' 243110595Smtm [ -n "$uexpire" ] && _expire='-e "$uexpire"' 244110595Smtm [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"' 245127076Smtm if [ -z "$Dflag" -a -n "$uhome" ]; then 246127076Smtm # The /nonexistent home directory is special. It 247127076Smtm # means the user has no home directory. 248127076Smtm if [ "$uhome" = "$NOHOME" ]; then 249127076Smtm _home='-d "$uhome"' 250127076Smtm else 251175719Smtm # Use home directory permissions if specified 252175719Smtm if [ -n "$uhomeperm" ]; then 253175719Smtm _home='-m -d "$uhome" -M "$uhomeperm"' 254175719Smtm else 255175719Smtm _home='-m -d "$uhome"' 256175719Smtm fi 257127076Smtm fi 258127076Smtm elif [ -n "$Dflag" -a -n "$uhome" ]; then 259127076Smtm _home='-d "$uhome"' 260127076Smtm fi 261107543Sscottl case $passwdtype in 262107543Sscottl no) 263107543Sscottl _passwdmethod="-w no" 264107543Sscottl _passwd="-h -" 265107543Sscottl ;; 266107543Sscottl yes) 267110595Smtm # Note on processing the password: The outer double quotes 268110595Smtm # make literal everything except ` and \ and $. 269110595Smtm # The outer single quotes make literal ` and $. 270110595Smtm # We can ensure the \ isn't treated specially by specifying 271110595Smtm # the -r switch to the read command used to obtain the input. 272110595Smtm # 273107543Sscottl _passwdmethod="-w yes" 274107543Sscottl _passwd="-h 0" 275110595Smtm _upasswd='echo "$upass" |' 276107543Sscottl ;; 277107543Sscottl none) 278107543Sscottl _passwdmethod="-w none" 279107543Sscottl ;; 280107543Sscottl random) 281107543Sscottl _passwdmethod="-w random" 282107543Sscottl ;; 283107543Sscottl esac 284107543Sscottl 285107543Sscottl _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment" 286107543Sscottl _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd" 287107543Sscottl _pwcmd="$_pwcmd $_expire $_pwexpire" 288107543Sscottl 289107543Sscottl if ! _output=`eval $_pwcmd` ; then 290107543Sscottl err "There was an error adding user ($username)." 291107543Sscottl return 1 292107543Sscottl else 293107543Sscottl info "Successfully added ($username) to the user database." 294107543Sscottl if [ "random" = "$passwdtype" ]; then 295107543Sscottl randompass="$_output" 296107543Sscottl info "Password for ($username) is: $randompass" 297107543Sscottl fi 298107543Sscottl fi 299107543Sscottl 300107543Sscottl if [ -n "$disableflag" ]; then 301107543Sscottl if ${PWCMD} lock $username ; then 302107543Sscottl info "Account ($username) is locked." 303107543Sscottl else 304107543Sscottl info "Account ($username) could NOT be locked." 305107543Sscottl fi 306107543Sscottl fi 307107543Sscottl 308107543Sscottl _line= 309107543Sscottl _owner= 310107543Sscottl _perms= 311107543Sscottl if [ -n "$msgflag" ]; then 312107543Sscottl [ -r "$msgfile" ] && { 313107543Sscottl # We're evaluating the contents of an external file. 314107543Sscottl # Let's not open ourselves up for attack. _perms will 315107543Sscottl # be empty if it's writeable only by the owner. _owner 316107543Sscottl # will *NOT* be empty if the file is owned by root. 317107543Sscottl # 318107543Sscottl _dir="`dirname $msgfile`" 319107543Sscottl _file="`basename $msgfile`" 320107543Sscottl _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune` 321107543Sscottl _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune` 322107543Sscottl if [ -z "$_owner" -o -n "$_perms" ]; then 323107543Sscottl err "The message file ($msgfile) may be writeable only by root." 324107543Sscottl return 1 325107543Sscottl fi 326107543Sscottl cat "$msgfile" | 327107543Sscottl while read _line ; do 328107543Sscottl eval echo "$_line" 329107543Sscottl done | ${MAILCMD} -s"Welcome" ${username} 330107543Sscottl info "Sent welcome message to ($username)." 331107543Sscottl } 332107543Sscottl fi 333107543Sscottl} 334107543Sscottl 335107543Sscottl# get_user 336107543Sscottl# Reads username of the account from standard input or from a global 337107543Sscottl# variable containing an account line from a file. The username is 338107543Sscottl# required. If this is an interactive session it will prompt in 339107543Sscottl# a loop until a username is entered. If it is batch processing from 340107543Sscottl# a file it will output an error message and return to the caller. 341107543Sscottl# 342107543Sscottlget_user() { 343107543Sscottl _input= 344107543Sscottl 345107543Sscottl # No need to take down user names if this is a configuration saving run. 346107543Sscottl [ -n "$configflag" ] && return 347107543Sscottl 348107543Sscottl while : ; do 349107543Sscottl if [ -z "$fflag" ]; then 350107543Sscottl echo -n "Username: " 351107543Sscottl read _input 352107543Sscottl else 353107543Sscottl _input="`echo "$fileline" | cut -f1 -d:`" 354107543Sscottl fi 355107543Sscottl 356167917Sle # There *must* be a username, and it must not exist. If 357167917Sle # this is an interactive session give the user an 358167917Sle # opportunity to retry. 359107543Sscottl # 360107543Sscottl if [ -z "$_input" ]; then 361107543Sscottl err "You must enter a username!" 362107543Sscottl [ -z "$fflag" ] && continue 363107543Sscottl fi 364167917Sle ${PWCMD} usershow $_input > /dev/null 2>&1 365167917Sle if [ "$?" -eq 0 ]; then 366167917Sle err "User exists!" 367167917Sle [ -z "$fflag" ] && continue 368167917Sle fi 369107543Sscottl break 370107543Sscottl done 371107543Sscottl username="$_input" 372107543Sscottl} 373107543Sscottl 374107543Sscottl# get_gecos 375107543Sscottl# Reads extra information about the user. Can be used both in interactive 376107543Sscottl# and batch (from file) mode. 377107543Sscottl# 378107543Sscottlget_gecos() { 379107543Sscottl _input= 380107543Sscottl 381107543Sscottl # No need to take down additional user information for a configuration run. 382107543Sscottl [ -n "$configflag" ] && return 383107543Sscottl 384107543Sscottl if [ -z "$fflag" ]; then 385107543Sscottl echo -n "Full name: " 386107543Sscottl read _input 387107543Sscottl else 388107543Sscottl _input="`echo "$fileline" | cut -f7 -d:`" 389107543Sscottl fi 390107543Sscottl ugecos="$_input" 391107543Sscottl} 392107543Sscottl 393107543Sscottl# get_shell 394107543Sscottl# Get the account's shell. Works in interactive and batch mode. It 395130160Smtm# accepts either the base name of the shell or the full path. 396107543Sscottl# If an invalid shell is entered it will simply use the default shell. 397107543Sscottl# 398107543Sscottlget_shell() { 399107543Sscottl _input= 400107543Sscottl _fullpath= 401107543Sscottl ushell="$defaultshell" 402107543Sscottl 403107543Sscottl # Make sure the current value of the shell is a valid one 404127076Smtm if [ -z "$Sflag" ]; then 405127076Smtm if ! shell_exists $ushell ; then 406127076Smtm info "Using default shell ${defaultshell}." 407127076Smtm ushell="$defaultshell" 408127076Smtm fi 409116627Smtm fi 410107543Sscottl 411107543Sscottl if [ -z "$fflag" ]; then 412107543Sscottl echo -n "Shell ($shells) [`basename $ushell`]: " 413107543Sscottl read _input 414107543Sscottl else 415107543Sscottl _input="`echo "$fileline" | cut -f9 -d:`" 416107543Sscottl fi 417107543Sscottl if [ -n "$_input" ]; then 418127076Smtm if [ -n "$Sflag" ]; then 419127076Smtm ushell="$_input" 420107543Sscottl else 421127076Smtm _fullpath=`fullpath_from_shell $_input` 422127076Smtm if [ -n "$_fullpath" ]; then 423127076Smtm ushell="$_fullpath" 424127076Smtm else 425127076Smtm err "Invalid shell ($_input) for user $username." 426127076Smtm info "Using default shell ${defaultshell}." 427127076Smtm ushell="$defaultshell" 428127076Smtm fi 429107543Sscottl fi 430107543Sscottl fi 431107543Sscottl} 432107543Sscottl 433107543Sscottl# get_homedir 434107543Sscottl# Reads the account's home directory. Used both with interactive input 435107543Sscottl# and batch input. 436107543Sscottl# 437107543Sscottlget_homedir() { 438107543Sscottl _input= 439107543Sscottl if [ -z "$fflag" ]; then 440107543Sscottl echo -n "Home directory [${homeprefix}/${username}]: " 441107543Sscottl read _input 442107543Sscottl else 443107543Sscottl _input="`echo "$fileline" | cut -f8 -d:`" 444107543Sscottl fi 445107543Sscottl 446107543Sscottl if [ -n "$_input" ]; then 447107543Sscottl uhome="$_input" 448107543Sscottl # if this is a configuration run, then user input is the home 449107543Sscottl # directory prefix. Otherwise it is understood to 450107543Sscottl # be $prefix/$user 451107543Sscottl # 452107543Sscottl [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome" 453107543Sscottl else 454107543Sscottl uhome="${homeprefix}/${username}" 455107543Sscottl fi 456107543Sscottl} 457107543Sscottl 458175719Smtm# get_homeperm 459175719Smtm# Reads the account's home directory permissions. 460175719Smtm# 461175719Smtmget_homeperm() { 462175719Smtm uhomeperm=$defaultHomePerm 463175719Smtm _input= 464175719Smtm _prompt= 465175719Smtm 466175719Smtm if [ -n "$uhomeperm" ]; then 467175719Smtm _prompt="Home directory permissions [${uhomeperm}]: " 468175719Smtm else 469175719Smtm _prompt="Home directory permissions (Leave empty for default): " 470175719Smtm fi 471175719Smtm if [ -z "$fflag" ]; then 472175719Smtm echo -n "$_prompt" 473175719Smtm read _input 474175719Smtm fi 475175719Smtm 476175719Smtm if [ -n "$_input" ]; then 477175719Smtm uhomeperm="$_input" 478175719Smtm fi 479175719Smtm} 480175719Smtm 481107543Sscottl# get_uid 482107543Sscottl# Reads a numeric userid in an interactive or batch session. Automatically 483107543Sscottl# allocates one if it is not specified. 484107543Sscottl# 485107543Sscottlget_uid() { 486168656Smtm uuid=${uidstart} 487107543Sscottl _input= 488107543Sscottl _prompt= 489107543Sscottl 490107543Sscottl if [ -n "$uuid" ]; then 491232146Sdeischen uuid=`get_nextuid $uuid` 492107543Sscottl _prompt="Uid [$uuid]: " 493107543Sscottl else 494107543Sscottl _prompt="Uid (Leave empty for default): " 495107543Sscottl fi 496107543Sscottl if [ -z "$fflag" ]; then 497109573Sfjoe echo -n "$_prompt" 498107543Sscottl read _input 499107543Sscottl else 500107543Sscottl _input="`echo "$fileline" | cut -f2 -d:`" 501107543Sscottl fi 502107543Sscottl 503107543Sscottl [ -n "$_input" ] && uuid=$_input 504107543Sscottl uuid=`get_nextuid $uuid` 505107543Sscottl uidstart=$uuid 506107543Sscottl} 507107543Sscottl 508107543Sscottl# get_class 509107543Sscottl# Reads login class of account. Can be used in interactive or batch mode. 510107543Sscottl# 511107543Sscottlget_class() { 512107543Sscottl uclass="$defaultclass" 513107543Sscottl _input= 514107543Sscottl _class=${uclass:-"default"} 515107543Sscottl 516107543Sscottl if [ -z "$fflag" ]; then 517107543Sscottl echo -n "Login class [$_class]: " 518107543Sscottl read _input 519107543Sscottl else 520107543Sscottl _input="`echo "$fileline" | cut -f4 -d:`" 521107543Sscottl fi 522107543Sscottl 523107543Sscottl [ -n "$_input" ] && uclass="$_input" 524107543Sscottl} 525107543Sscottl 526107543Sscottl# get_logingroup 527107543Sscottl# Reads user's login group. Can be used in both interactive and batch 528107543Sscottl# modes. The specified value can be a group name or its numeric id. 529112433Smtm# This routine leaves the field blank if nothing is provided and 530112433Smtm# a default login group has not been set. The pw(8) command 531112433Smtm# will then provide a login group with the same name as the username. 532107543Sscottl# 533107543Sscottlget_logingroup() { 534112433Smtm ulogingroup="$defaultLgroup" 535107543Sscottl _input= 536107543Sscottl 537107543Sscottl if [ -z "$fflag" ]; then 538112433Smtm echo -n "Login group [${ulogingroup:-$username}]: " 539107543Sscottl read _input 540107543Sscottl else 541107543Sscottl _input="`echo "$fileline" | cut -f3 -d:`" 542107543Sscottl fi 543107543Sscottl 544107543Sscottl # Pw(8) will use the username as login group if it's left empty 545112433Smtm [ -n "$_input" ] && ulogingroup="$_input" 546107543Sscottl} 547107543Sscottl 548107543Sscottl# get_groups 549107543Sscottl# Read additional groups for the user. It can be used in both interactive 550107543Sscottl# and batch modes. 551107543Sscottl# 552107543Sscottlget_groups() { 553107543Sscottl ugroups="$defaultgroups" 554107543Sscottl _input= 555107543Sscottl _group=${ulogingroup:-"${username}"} 556107543Sscottl 557107543Sscottl if [ -z "$configflag" ]; then 558107543Sscottl [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username" 559107543Sscottl [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: " 560107543Sscottl else 561107543Sscottl [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: " 562107543Sscottl fi 563107543Sscottl read _input 564107543Sscottl 565107543Sscottl [ -n "$_input" ] && ugroups="$_input" 566107543Sscottl} 567107543Sscottl 568107543Sscottl# get_expire_dates 569107543Sscottl# Read expiry information for the account and also for the password. This 570107543Sscottl# routine is used only from batch processing mode. 571107543Sscottl# 572107543Sscottlget_expire_dates() { 573107543Sscottl upwexpire="`echo "$fileline" | cut -f5 -d:`" 574107543Sscottl uexpire="`echo "$fileline" | cut -f6 -d:`" 575107543Sscottl} 576107543Sscottl 577107543Sscottl# get_password 578107543Sscottl# Read the password in batch processing mode. The password field matters 579107543Sscottl# only when the password type is "yes" or "random". If the field is empty and the 580107543Sscottl# password type is "yes", then it assumes the account has an empty passsword 581107543Sscottl# and changes the password type accordingly. If the password type is "random" 582107543Sscottl# and the password field is NOT empty, then it assumes the account will NOT 583107543Sscottl# have a random password and set passwdtype to "yes." 584107543Sscottl# 585107543Sscottlget_password() { 586107543Sscottl # We may temporarily change a password type. Make sure it's changed 587107543Sscottl # back to whatever it was before we process the next account. 588107543Sscottl # 589107543Sscottl [ -n "$savedpwtype" ] && { 590107543Sscottl passwdtype=$savedpwtype 591107543Sscottl savedpwtype= 592107543Sscottl } 593107543Sscottl 594107543Sscottl # There may be a ':' in the password 595107543Sscottl upass=${fileline#*:*:*:*:*:*:*:*:*:} 596107543Sscottl 597107543Sscottl if [ -z "$upass" ]; then 598107543Sscottl case $passwdtype in 599107543Sscottl yes) 600107543Sscottl # if it's empty, assume an empty password 601107543Sscottl passwdtype=none 602107543Sscottl savedpwtype=yes 603107543Sscottl ;; 604107543Sscottl esac 605107543Sscottl else 606107543Sscottl case $passwdtype in 607107543Sscottl random) 608107543Sscottl passwdtype=yes 609107543Sscottl savedpwtype=random 610107543Sscottl ;; 611107543Sscottl esac 612107543Sscottl fi 613107543Sscottl} 614107543Sscottl 615107543Sscottl# input_from_file 616107543Sscottl# Reads a line of account information from standard input and 617107543Sscottl# adds it to the user database. 618107543Sscottl# 619107543Sscottlinput_from_file() { 620107543Sscottl _field= 621107543Sscottl 622110595Smtm while read -r fileline ; do 623107543Sscottl case "$fileline" in 624107543Sscottl \#*|'') 625107543Sscottl ;; 626168651Smtm *) 627168651Smtm get_user || continue 628168651Smtm get_gecos 629168651Smtm get_uid 630168651Smtm get_logingroup 631168651Smtm get_class 632168651Smtm get_shell 633168651Smtm get_homedir 634175719Smtm get_homeperm 635168651Smtm get_password 636168651Smtm get_expire_dates 637172823Smtm ugroups="$defaultgroups" 638168651Smtm 639168651Smtm add_user 640168651Smtm ;; 641107543Sscottl esac 642107543Sscottl done 643107543Sscottl} 644107543Sscottl 645107543Sscottl# input_interactive 646107543Sscottl# Prompts for user information interactively, and commits to 647107543Sscottl# the user database. 648107543Sscottl# 649107543Sscottlinput_interactive() { 650107543Sscottl _disable= 651107543Sscottl _pass= 652107543Sscottl _passconfirm= 653107543Sscottl _random="no" 654107543Sscottl _emptypass="no" 655107543Sscottl _usepass="yes" 656112401Smtm _logingroup_ok="no" 657112401Smtm _groups_ok="no" 658107543Sscottl case $passwdtype in 659107543Sscottl none) 660107543Sscottl _emptypass="yes" 661107543Sscottl _usepass="yes" 662107543Sscottl ;; 663107543Sscottl no) 664107543Sscottl _usepass="no" 665107543Sscottl ;; 666107543Sscottl random) 667107543Sscottl _random="yes" 668107543Sscottl ;; 669107543Sscottl esac 670107543Sscottl 671107543Sscottl get_user 672107543Sscottl get_gecos 673107543Sscottl get_uid 674110537Sadrian 675110537Sadrian # The case where group = user is handled elsewhere, so 676110537Sadrian # validate any other groups the user is invited to. 677110537Sadrian until [ "$_logingroup_ok" = yes ]; do 678110537Sadrian get_logingroup 679110537Sadrian _logingroup_ok=yes 680110537Sadrian if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then 681110537Sadrian if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then 682110537Sadrian echo "Group $ulogingroup does not exist!" 683110537Sadrian _logingroup_ok=no 684110537Sadrian fi 685110537Sadrian fi 686110537Sadrian done 687110537Sadrian until [ "$_groups_ok" = yes ]; do 688110537Sadrian get_groups 689110537Sadrian _groups_ok=yes 690110537Sadrian for i in $ugroups; do 691110537Sadrian if [ "$username" != "$i" ]; then 692110537Sadrian if ! ${PWCMD} show group $i > /dev/null 2>&1; then 693110537Sadrian echo "Group $i does not exist!" 694110537Sadrian _groups_ok=no 695110537Sadrian fi 696110537Sadrian fi 697110537Sadrian done 698110537Sadrian done 699110537Sadrian 700107543Sscottl get_class 701107543Sscottl get_shell 702107543Sscottl get_homedir 703175719Smtm get_homeperm 704107543Sscottl 705107543Sscottl while : ; do 706107543Sscottl echo -n "Use password-based authentication? [$_usepass]: " 707107543Sscottl read _input 708107543Sscottl [ -z "$_input" ] && _input=$_usepass 709107543Sscottl case $_input in 710107543Sscottl [Nn][Oo]|[Nn]) 711107543Sscottl passwdtype="no" 712107543Sscottl ;; 713107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 714107543Sscottl while : ; do 715107543Sscottl echo -n "Use an empty password? (yes/no) [$_emptypass]: " 716107543Sscottl read _input 717107543Sscottl [ -n "$_input" ] && _emptypass=$_input 718107543Sscottl case $_emptypass in 719107543Sscottl [Nn][Oo]|[Nn]) 720107543Sscottl echo -n "Use a random password? (yes/no) [$_random]: " 721107543Sscottl read _input 722107543Sscottl [ -n "$_input" ] && _random="$_input" 723107543Sscottl case $_random in 724107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 725107543Sscottl passwdtype="random" 726107543Sscottl break 727107543Sscottl ;; 728107543Sscottl esac 729107543Sscottl passwdtype="yes" 730112401Smtm [ -n "$configflag" ] && break 731107543Sscottl trap 'stty echo; exit' 0 1 2 3 15 732107543Sscottl stty -echo 733107543Sscottl echo -n "Enter password: " 734110595Smtm read -r upass 735107543Sscottl echo'' 736107543Sscottl echo -n "Enter password again: " 737116623Smtm read -r _passconfirm 738107543Sscottl echo '' 739107543Sscottl stty echo 740107543Sscottl # if user entered a blank password 741107543Sscottl # explicitly ask again. 742107543Sscottl [ -z "$upass" -a -z "$_passconfirm" ] \ 743107543Sscottl && continue 744107543Sscottl ;; 745107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 746107543Sscottl passwdtype="none" 747107543Sscottl break; 748107543Sscottl ;; 749107543Sscottl *) 750107543Sscottl # invalid answer; repeat the loop 751107543Sscottl continue 752107543Sscottl ;; 753107543Sscottl esac 754107543Sscottl if [ "$upass" != "$_passconfirm" ]; then 755107543Sscottl echo "Passwords did not match!" 756107543Sscottl continue 757107543Sscottl fi 758107543Sscottl break 759107543Sscottl done 760107543Sscottl ;; 761107543Sscottl *) 762107543Sscottl # invalid answer; repeat loop 763107543Sscottl continue 764107543Sscottl ;; 765107543Sscottl esac 766107543Sscottl break; 767107543Sscottl done 768107543Sscottl _disable=${disableflag:-"no"} 769107543Sscottl while : ; do 770107543Sscottl echo -n "Lock out the account after creation? [$_disable]: " 771107543Sscottl read _input 772107543Sscottl [ -z "$_input" ] && _input=$_disable 773107543Sscottl case $_input in 774107543Sscottl [Nn][Oo]|[Nn]) 775107543Sscottl disableflag= 776107543Sscottl ;; 777107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 778107543Sscottl disableflag=yes 779107543Sscottl ;; 780107543Sscottl *) 781107543Sscottl # invalid answer; repeat loop 782107543Sscottl continue 783107543Sscottl ;; 784107543Sscottl esac 785107543Sscottl break 786107543Sscottl done 787107543Sscottl 788107543Sscottl # Display the information we have so far and prompt to 789107543Sscottl # commit it. 790107543Sscottl # 791107543Sscottl _disable=${disableflag:-"no"} 792107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username 793107543Sscottl case $passwdtype in 794107543Sscottl yes) 795107543Sscottl _pass='*****' 796107543Sscottl ;; 797107543Sscottl no) 798107543Sscottl _pass='<disabled>' 799107543Sscottl ;; 800107543Sscottl none) 801107543Sscottl _pass='<blank>' 802107543Sscottl ;; 803107543Sscottl random) 804107543Sscottl _pass='<random>' 805107543Sscottl ;; 806107543Sscottl esac 807109751Sfjoe [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass" 808109751Sfjoe [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype" 809107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos" 810107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid" 811107543Sscottl printf "%-10s : %s\n" "Class" "$uclass" 812112433Smtm printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups" 813107543Sscottl printf "%-10s : %s\n" "Home" "$uhome" 814175719Smtm printf "%-10s : %s\n" "Home Mode" "$uhomeperm" 815107543Sscottl printf "%-10s : %s\n" "Shell" "$ushell" 816107543Sscottl printf "%-10s : %s\n" "Locked" "$_disable" 817107543Sscottl while : ; do 818107543Sscottl echo -n "OK? (yes/no): " 819107543Sscottl read _input 820107543Sscottl case $_input in 821107543Sscottl [Nn][Oo]|[Nn]) 822107543Sscottl return 1 823107543Sscottl ;; 824107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 825107543Sscottl add_user 826107543Sscottl ;; 827107543Sscottl *) 828107543Sscottl continue 829107543Sscottl ;; 830107543Sscottl esac 831107543Sscottl break 832107543Sscottl done 833107543Sscottl return 0 834107543Sscottl} 835107543Sscottl 836145618Srobert#### END SUBROUTINE DEFINITION #### 837107543Sscottl 838107543SscottlTHISCMD=`/usr/bin/basename $0` 839107543SscottlDEFAULTSHELL=/bin/sh 840107543SscottlADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}" 841107543SscottlPWCMD="${PWCMD:-/usr/sbin/pw}" 842107543SscottlMAILCMD="${MAILCMD:-mail}" 843107543SscottlETCSHELLS="${ETCSHELLS:-/etc/shells}" 844127076SmtmNOHOME="/nonexistent" 845116627SmtmNOLOGIN="nologin" 846127635ScpercivaNOLOGIN_PATH="/usr/sbin/nologin" 847109751SfjoeGREPCMD="/usr/bin/grep" 848109751SfjoeDATECMD="/bin/date" 849107543Sscottl 850107543Sscottl# Set default values 851107543Sscottl# 852107543Sscottlusername= 853107543Sscottluuid= 854107543Sscottluidstart= 855107543Sscottlugecos= 856107543Sscottlulogingroup= 857107543Sscottluclass= 858107543Sscottluhome= 859175719Smtmuhomeperm= 860107543Sscottlupass= 861107543Sscottlushell= 862107543Sscottludotdir=/usr/share/skel 863107543Sscottlugroups= 864107543Sscottluexpire= 865107543Sscottlupwexpire= 866107543Sscottlshells="`valid_shells`" 867107543Sscottlpasswdtype="yes" 868107543Sscottlmsgfile=/etc/adduser.msg 869107543Sscottlmsgflag= 870107543Sscottlquietflag= 871107543Sscottlconfigflag= 872107543Sscottlfflag= 873107543Sscottlinfile= 874107543Sscottldisableflag= 875127076SmtmDflag= 876127076SmtmSflag= 877107543Sscottlreadconfig="yes" 878107543Sscottlhomeprefix="/home" 879107543Sscottlrandompass= 880107543Sscottlfileline= 881107543Sscottlsavedpwtype= 882107543Sscottldefaultclass= 883112433SmtmdefaultLgroup= 884116784Smtmdefaultgroups= 885107543Sscottldefaultshell="${DEFAULTSHELL}" 886175719SmtmdefaultHomePerm= 887107543Sscottl 888107543Sscottl# Make sure the user running this program is root. This isn't a security 889202312Sjoel# measure as much as it is a useful method of reminding the user to 890107543Sscottl# 'su -' before he/she wastes time entering data that won't be saved. 891107543Sscottl# 892107543Sscottlprocowner=${procowner:-`/usr/bin/id -u`} 893107543Sscottlif [ "$procowner" != "0" ]; then 894107543Sscottl err 'you must be the super-user (uid 0) to use this utility.' 895107543Sscottl exit 1 896107543Sscottlfi 897107543Sscottl 898228990Suqs# Override from our conf file 899107543Sscottl# Quickly go through the commandline line to see if we should read 900107543Sscottl# from our configuration file. The actual parsing of the commandline 901107543Sscottl# arguments happens after we read in our configuration file (commandline 902107543Sscottl# should override configuration file). 903107543Sscottl# 904107543Sscottlfor _i in $* ; do 905107543Sscottl if [ "$_i" = "-N" ]; then 906107543Sscottl readconfig= 907107543Sscottl break; 908107543Sscottl fi 909107543Sscottldone 910107543Sscottlif [ -n "$readconfig" ]; then 911107543Sscottl # On a long-lived system, the first time this script is run it 912107543Sscottl # will barf upon reading the configuration file for its perl predecessor. 913107543Sscottl if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then 914107543Sscottl [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1 915107543Sscottl fi 916107543Sscottlfi 917107543Sscottl 918228990Suqs# Process command-line options 919107543Sscottl# 920107543Sscottlfor _switch ; do 921107543Sscottl case $_switch in 922107543Sscottl -L) 923107543Sscottl defaultclass="$2" 924107543Sscottl shift; shift 925107543Sscottl ;; 926107543Sscottl -C) 927107543Sscottl configflag=yes 928107543Sscottl shift 929107543Sscottl ;; 930127076Smtm -D) 931127076Smtm Dflag=yes 932127076Smtm shift 933127076Smtm ;; 934107543Sscottl -E) 935107543Sscottl disableflag=yes 936107543Sscottl shift 937107543Sscottl ;; 938107543Sscottl -k) 939107543Sscottl udotdir="$2" 940107543Sscottl shift; shift 941107543Sscottl ;; 942107543Sscottl -f) 943107543Sscottl [ "$2" != "-" ] && infile="$2" 944107543Sscottl fflag=yes 945107543Sscottl shift; shift 946107543Sscottl ;; 947112433Smtm -g) 948112433Smtm defaultLgroup="$2" 949112433Smtm shift; shift 950112433Smtm ;; 951107543Sscottl -G) 952107543Sscottl defaultgroups="$2" 953107543Sscottl shift; shift 954107543Sscottl ;; 955107543Sscottl -h) 956107543Sscottl show_usage 957107543Sscottl exit 0 958107543Sscottl ;; 959107543Sscottl -d) 960107543Sscottl homeprefix="$2" 961107543Sscottl shift; shift 962107543Sscottl ;; 963107543Sscottl -m) 964107543Sscottl case "$2" in 965107543Sscottl [Nn][Oo]) 966107543Sscottl msgflag= 967107543Sscottl ;; 968107543Sscottl *) 969107543Sscottl msgflag=yes 970107543Sscottl msgfile="$2" 971107543Sscottl ;; 972107543Sscottl esac 973107543Sscottl shift; shift 974107543Sscottl ;; 975175719Smtm -M) 976175719Smtm defaultHomePerm=$2 977175719Smtm shift; shift 978175719Smtm ;; 979107543Sscottl -N) 980107543Sscottl readconfig= 981107543Sscottl shift 982107543Sscottl ;; 983107543Sscottl -w) 984107543Sscottl case "$2" in 985107543Sscottl no|none|random|yes) 986107543Sscottl passwdtype=$2 987107543Sscottl ;; 988107543Sscottl *) 989107543Sscottl show_usage 990107543Sscottl exit 1 991107543Sscottl ;; 992107543Sscottl esac 993107543Sscottl shift; shift 994107543Sscottl ;; 995107543Sscottl -q) 996107543Sscottl quietflag=yes 997107543Sscottl shift 998107543Sscottl ;; 999107543Sscottl -s) 1000107543Sscottl defaultshell="`fullpath_from_shell $2`" 1001107543Sscottl shift; shift 1002107543Sscottl ;; 1003127076Smtm -S) 1004127076Smtm Sflag=yes 1005127076Smtm shift 1006127076Smtm ;; 1007107543Sscottl -u) 1008107543Sscottl uidstart=$2 1009107543Sscottl shift; shift 1010107543Sscottl ;; 1011107543Sscottl esac 1012107543Sscottldone 1013107543Sscottl 1014107543Sscottl# If the -f switch was used, get input from a file. Otherwise, 1015107543Sscottl# this is an interactive session. 1016107543Sscottl# 1017107543Sscottlif [ -n "$fflag" ]; then 1018107543Sscottl if [ -z "$infile" ]; then 1019107543Sscottl input_from_file 1020107543Sscottl elif [ -n "$infile" ]; then 1021107543Sscottl if [ -r "$infile" ]; then 1022107543Sscottl input_from_file < $infile 1023107543Sscottl else 1024107543Sscottl err "File ($infile) is unreadable or does not exist." 1025107543Sscottl fi 1026107543Sscottl fi 1027107543Sscottlelse 1028107543Sscottl input_interactive 1029109768Smtm while : ; do 1030112401Smtm if [ -z "$configflag" ]; then 1031112401Smtm echo -n "Add another user? (yes/no): " 1032112401Smtm else 1033112401Smtm echo -n "Re-edit the default configuration? (yes/no): " 1034112401Smtm fi 1035109768Smtm read _input 1036109768Smtm case $_input in 1037109768Smtm [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 1038109768Smtm uidstart=`get_nextuid $uidstart` 1039109768Smtm input_interactive 1040109768Smtm continue 1041109768Smtm ;; 1042109768Smtm [Nn][Oo]|[Nn]) 1043109768Smtm echo "Goodbye!" 1044109768Smtm ;; 1045109768Smtm *) 1046109768Smtm continue 1047109768Smtm ;; 1048109768Smtm esac 1049109768Smtm break 1050109768Smtm done 1051107543Sscottlfi 1052