1107543Sscottl#!/bin/sh 2107543Sscottl# 3330449Seadler# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 4330449Seadler# 5127076Smtm# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved. 6107543Sscottl# 7107543Sscottl# Redistribution and use in source and binary forms, with or without 8107543Sscottl# modification, are permitted provided that the following conditions 9107543Sscottl# are met: 10107543Sscottl# 1. Redistributions of source code must retain the above copyright 11107543Sscottl# notice, this list of conditions and the following disclaimer. 12107543Sscottl# 2. Redistributions in binary form must reproduce the above copyright 13107543Sscottl# notice, this list of conditions and the following disclaimer in the 14107543Sscottl# documentation and/or other materials provided with the distribution. 15107543Sscottl# 16107543Sscottl# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17107543Sscottl# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18107543Sscottl# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19107543Sscottl# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20107543Sscottl# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21107543Sscottl# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22107543Sscottl# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23107543Sscottl# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24107543Sscottl# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25107543Sscottl# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26107543Sscottl# 27116624Smtm# Email: Mike Makonnen <mtm@FreeBSD.Org> 28107543Sscottl# 29107543Sscottl# $FreeBSD: stable/11/usr.sbin/adduser/adduser.sh 360390 2020-04-27 19:29:48Z kevans $ 30107543Sscottl# 31107543Sscottl 32107543Sscottl# err msg 33202312Sjoel# Display $msg on stderr, unless we're being quiet. 34202312Sjoel# 35107543Sscottlerr() { 36107543Sscottl if [ -z "$quietflag" ]; then 37202312Sjoel echo 1>&2 ${THISCMD}: ERROR: $* 38107543Sscottl fi 39107543Sscottl} 40107543Sscottl 41107543Sscottl# info msg 42202312Sjoel# Display $msg on stdout, unless we're being quiet. 43202312Sjoel# 44107543Sscottlinfo() { 45107543Sscottl if [ -z "$quietflag" ]; then 46202312Sjoel echo ${THISCMD}: INFO: $* 47107543Sscottl fi 48107543Sscottl} 49107543Sscottl 50107543Sscottl# get_nextuid 51107543Sscottl# Output the value of $_uid if it is available for use. If it 52107543Sscottl# is not, output the value of the next higher uid that is available. 53107543Sscottl# If a uid is not specified, output the first available uid, as indicated 54107543Sscottl# by pw(8). 55202312Sjoel# 56107543Sscottlget_nextuid () { 57107543Sscottl _uid=$1 58107543Sscottl _nextuid= 59107543Sscottl 60107543Sscottl if [ -z "$_uid" ]; then 61107543Sscottl _nextuid="`${PWCMD} usernext | cut -f1 -d:`" 62107543Sscottl else 63107543Sscottl while : ; do 64107543Sscottl ${PWCMD} usershow $_uid > /dev/null 2>&1 65107543Sscottl if [ ! "$?" -eq 0 ]; then 66107543Sscottl _nextuid=$_uid 67107543Sscottl break 68107543Sscottl fi 69107543Sscottl _uid=$(($_uid + 1)) 70107543Sscottl done 71107543Sscottl fi 72107543Sscottl echo $_nextuid 73107543Sscottl} 74107543Sscottl 75107543Sscottl# show_usage 76107543Sscottl# Display usage information for this utility. 77107543Sscottl# 78107543Sscottlshow_usage() { 79107543Sscottl echo "usage: ${THISCMD} [options]" 80107543Sscottl echo " options may include:" 81107543Sscottl echo " -C save to the configuration file only" 82127076Smtm echo " -D do not attempt to create the home directory" 83107543Sscottl echo " -E disable this account after creation" 84107543Sscottl echo " -G additional groups to add accounts to" 85107543Sscottl echo " -L login class of the user" 86175719Smtm echo " -M file permission for home directory" 87107543Sscottl echo " -N do not read configuration file" 88127076Smtm echo " -S a nonexistent shell is not an error" 89107543Sscottl echo " -d home directory" 90107543Sscottl echo " -f file from which input will be received" 91112519Smtm echo " -g default login group" 92107543Sscottl echo " -h display this usage message" 93107543Sscottl echo " -k path to skeleton home directory" 94107543Sscottl echo " -m user welcome message file" 95107543Sscottl echo " -q absolute minimal user feedback" 96107543Sscottl echo " -s shell" 97107543Sscottl echo " -u uid to start at" 98107543Sscottl echo " -w password type: no, none, yes or random" 99107543Sscottl} 100107543Sscottl 101107543Sscottl# valid_shells 102107543Sscottl# Outputs a list of valid shells from /etc/shells. Only the 103107543Sscottl# basename of the shell is output. 104107543Sscottl# 105107543Sscottlvalid_shells() { 106107543Sscottl _prefix= 107107543Sscottl cat ${ETCSHELLS} | 108107543Sscottl while read _path _junk ; do 109107543Sscottl case $_path in 110107543Sscottl \#*|'') 111107543Sscottl ;; 112107543Sscottl *) 113107543Sscottl echo -n "${_prefix}`basename $_path`" 114107543Sscottl _prefix=' ' 115107543Sscottl ;; 116107543Sscottl esac 117107543Sscottl done 118116627Smtm 119127635Scperciva # /usr/sbin/nologin is a special case 120116627Smtm [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}" 121107543Sscottl} 122107543Sscottl 123107543Sscottl# fullpath_from_shell shell 124130160Smtm# Given $shell, which is either the full path to a shell or 125130160Smtm# the basename component of a valid shell, get the 126107543Sscottl# full path to the shell from the /etc/shells file. 127107543Sscottl# 128107543Sscottlfullpath_from_shell() { 129107543Sscottl _shell=$1 130107543Sscottl [ -z "$_shell" ] && return 1 131107543Sscottl 132135616Sroam # /usr/sbin/nologin is a special case; it needs to be handled 133135616Sroam # before the cat | while loop, since a 'return' from within 134135616Sroam # a subshell will not terminate the function's execution, and 135135616Sroam # the path to the nologin shell might be printed out twice. 136135616Sroam # 137135616Sroam if [ "$_shell" = "${NOLOGIN}" -o \ 138135616Sroam "$_shell" = "${NOLOGIN_PATH}" ]; then 139135616Sroam echo ${NOLOGIN_PATH} 140135616Sroam return 0; 141135616Sroam fi 142135616Sroam 143107543Sscottl cat ${ETCSHELLS} | 144107543Sscottl while read _path _junk ; do 145107543Sscottl case "$_path" in 146107543Sscottl \#*|'') 147107543Sscottl ;; 148107543Sscottl *) 149130160Smtm if [ "$_path" = "$_shell" -o \ 150130160Smtm "`basename $_path`" = "$_shell" ]; then 151107543Sscottl echo $_path 152107543Sscottl return 0 153107543Sscottl fi 154107543Sscottl ;; 155107543Sscottl esac 156107543Sscottl done 157116627Smtm 158107543Sscottl return 1 159107543Sscottl} 160107543Sscottl 161116627Smtm# shell_exists shell 162116627Smtm# If the given shell is listed in ${ETCSHELLS} or it is 163116627Smtm# the nologin shell this function will return 0. 164116627Smtm# Otherwise, it will return 1. If shell is valid but 165116627Smtm# the path is invalid or it is not executable it 166116627Smtm# will emit an informational message saying so. 167116627Smtm# 168202312Sjoelshell_exists() { 169116627Smtm _sh="$1" 170116627Smtm _shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1" 171116627Smtm 172116627Smtm if ! eval $_shellchk; then 173116627Smtm # The nologin shell is not listed in /etc/shells. 174116627Smtm if [ "$_sh" != "${NOLOGIN_PATH}" ]; then 175116627Smtm err "Invalid shell ($_sh) for user $username." 176116627Smtm return 1 177116627Smtm fi 178116627Smtm fi 179116627Smtm ! [ -x "$_sh" ] && 180116628Smtm info "The shell ($_sh) does not exist or is not executable." 181116627Smtm 182116627Smtm return 0 183116627Smtm} 184116627Smtm 185107543Sscottl# save_config 186107543Sscottl# Save some variables to a configuration file. 187107543Sscottl# Note: not all script variables are saved, only those that 188107543Sscottl# it makes sense to save. 189107543Sscottl# 190107543Sscottlsave_config() { 191107543Sscottl echo "# Configuration file for adduser(8)." > ${ADDUSERCONF} 192107543Sscottl echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF} 193109751Sfjoe echo "# Last Modified on `${DATECMD}`." >> ${ADDUSERCONF} 194107543Sscottl echo '' >> ${ADDUSERCONF} 195175719Smtm echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF} 196112433Smtm echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF} 197107543Sscottl echo "defaultclass=$uclass" >> ${ADDUSERCONF} 198107543Sscottl echo "defaultgroups=$ugroups" >> ${ADDUSERCONF} 199107543Sscottl echo "passwdtype=$passwdtype" >> ${ADDUSERCONF} 200107543Sscottl echo "homeprefix=$homeprefix" >> ${ADDUSERCONF} 201107543Sscottl echo "defaultshell=$ushell" >> ${ADDUSERCONF} 202107543Sscottl echo "udotdir=$udotdir" >> ${ADDUSERCONF} 203107543Sscottl echo "msgfile=$msgfile" >> ${ADDUSERCONF} 204107543Sscottl echo "disableflag=$disableflag" >> ${ADDUSERCONF} 205168656Smtm echo "uidstart=$uidstart" >> ${ADDUSERCONF} 206107543Sscottl} 207107543Sscottl 208107543Sscottl# add_user 209107543Sscottl# Add a user to the user database. If the user chose to send a welcome 210107543Sscottl# message or lock the account, do so. 211107543Sscottl# 212107543Sscottladd_user() { 213107543Sscottl 214107543Sscottl # Is this a configuration run? If so, don't modify user database. 215107543Sscottl # 216107543Sscottl if [ -n "$configflag" ]; then 217107543Sscottl save_config 218107543Sscottl return 219107543Sscottl fi 220107543Sscottl 221107543Sscottl _uid= 222107543Sscottl _name= 223107543Sscottl _comment= 224107543Sscottl _gecos= 225107543Sscottl _home= 226107543Sscottl _group= 227107543Sscottl _grouplist= 228107543Sscottl _shell= 229107543Sscottl _class= 230107543Sscottl _dotdir= 231107543Sscottl _expire= 232107543Sscottl _pwexpire= 233107543Sscottl _passwd= 234107543Sscottl _upasswd= 235107543Sscottl _passwdmethod= 236107543Sscottl 237109720Smtm _name="-n '$username'" 238110595Smtm [ -n "$uuid" ] && _uid='-u "$uuid"' 239110595Smtm [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"' 240110595Smtm [ -n "$ugroups" ] && _grouplist='-G "$ugroups"' 241110595Smtm [ -n "$ushell" ] && _shell='-s "$ushell"' 242110595Smtm [ -n "$uclass" ] && _class='-L "$uclass"' 243110595Smtm [ -n "$ugecos" ] && _comment='-c "$ugecos"' 244110595Smtm [ -n "$udotdir" ] && _dotdir='-k "$udotdir"' 245110595Smtm [ -n "$uexpire" ] && _expire='-e "$uexpire"' 246110595Smtm [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"' 247127076Smtm if [ -z "$Dflag" -a -n "$uhome" ]; then 248127076Smtm # The /nonexistent home directory is special. It 249127076Smtm # means the user has no home directory. 250127076Smtm if [ "$uhome" = "$NOHOME" ]; then 251127076Smtm _home='-d "$uhome"' 252127076Smtm else 253175719Smtm # Use home directory permissions if specified 254175719Smtm if [ -n "$uhomeperm" ]; then 255175719Smtm _home='-m -d "$uhome" -M "$uhomeperm"' 256175719Smtm else 257175719Smtm _home='-m -d "$uhome"' 258175719Smtm fi 259127076Smtm fi 260127076Smtm elif [ -n "$Dflag" -a -n "$uhome" ]; then 261127076Smtm _home='-d "$uhome"' 262127076Smtm fi 263107543Sscottl case $passwdtype in 264107543Sscottl no) 265107543Sscottl _passwdmethod="-w no" 266107543Sscottl _passwd="-h -" 267107543Sscottl ;; 268107543Sscottl yes) 269110595Smtm # Note on processing the password: The outer double quotes 270110595Smtm # make literal everything except ` and \ and $. 271110595Smtm # The outer single quotes make literal ` and $. 272110595Smtm # We can ensure the \ isn't treated specially by specifying 273110595Smtm # the -r switch to the read command used to obtain the input. 274110595Smtm # 275107543Sscottl _passwdmethod="-w yes" 276107543Sscottl _passwd="-h 0" 277110595Smtm _upasswd='echo "$upass" |' 278107543Sscottl ;; 279107543Sscottl none) 280107543Sscottl _passwdmethod="-w none" 281107543Sscottl ;; 282107543Sscottl random) 283107543Sscottl _passwdmethod="-w random" 284107543Sscottl ;; 285107543Sscottl esac 286107543Sscottl 287107543Sscottl _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment" 288107543Sscottl _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd" 289107543Sscottl _pwcmd="$_pwcmd $_expire $_pwexpire" 290107543Sscottl 291107543Sscottl if ! _output=`eval $_pwcmd` ; then 292107543Sscottl err "There was an error adding user ($username)." 293107543Sscottl return 1 294107543Sscottl else 295107543Sscottl info "Successfully added ($username) to the user database." 296107543Sscottl if [ "random" = "$passwdtype" ]; then 297107543Sscottl randompass="$_output" 298107543Sscottl info "Password for ($username) is: $randompass" 299107543Sscottl fi 300107543Sscottl fi 301107543Sscottl 302107543Sscottl if [ -n "$disableflag" ]; then 303107543Sscottl if ${PWCMD} lock $username ; then 304107543Sscottl info "Account ($username) is locked." 305107543Sscottl else 306107543Sscottl info "Account ($username) could NOT be locked." 307107543Sscottl fi 308107543Sscottl fi 309107543Sscottl 310107543Sscottl _line= 311107543Sscottl _owner= 312107543Sscottl _perms= 313107543Sscottl if [ -n "$msgflag" ]; then 314107543Sscottl [ -r "$msgfile" ] && { 315107543Sscottl # We're evaluating the contents of an external file. 316107543Sscottl # Let's not open ourselves up for attack. _perms will 317107543Sscottl # be empty if it's writeable only by the owner. _owner 318107543Sscottl # will *NOT* be empty if the file is owned by root. 319107543Sscottl # 320107543Sscottl _dir="`dirname $msgfile`" 321107543Sscottl _file="`basename $msgfile`" 322107543Sscottl _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune` 323107543Sscottl _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune` 324107543Sscottl if [ -z "$_owner" -o -n "$_perms" ]; then 325107543Sscottl err "The message file ($msgfile) may be writeable only by root." 326107543Sscottl return 1 327107543Sscottl fi 328107543Sscottl cat "$msgfile" | 329107543Sscottl while read _line ; do 330107543Sscottl eval echo "$_line" 331107543Sscottl done | ${MAILCMD} -s"Welcome" ${username} 332107543Sscottl info "Sent welcome message to ($username)." 333107543Sscottl } 334107543Sscottl fi 335107543Sscottl} 336107543Sscottl 337107543Sscottl# get_user 338107543Sscottl# Reads username of the account from standard input or from a global 339107543Sscottl# variable containing an account line from a file. The username is 340107543Sscottl# required. If this is an interactive session it will prompt in 341107543Sscottl# a loop until a username is entered. If it is batch processing from 342107543Sscottl# a file it will output an error message and return to the caller. 343107543Sscottl# 344107543Sscottlget_user() { 345107543Sscottl _input= 346107543Sscottl 347107543Sscottl # No need to take down user names if this is a configuration saving run. 348107543Sscottl [ -n "$configflag" ] && return 349107543Sscottl 350107543Sscottl while : ; do 351107543Sscottl if [ -z "$fflag" ]; then 352107543Sscottl echo -n "Username: " 353107543Sscottl read _input 354107543Sscottl else 355107543Sscottl _input="`echo "$fileline" | cut -f1 -d:`" 356107543Sscottl fi 357107543Sscottl 358167917Sle # There *must* be a username, and it must not exist. If 359167917Sle # this is an interactive session give the user an 360167917Sle # opportunity to retry. 361107543Sscottl # 362107543Sscottl if [ -z "$_input" ]; then 363107543Sscottl err "You must enter a username!" 364107543Sscottl [ -z "$fflag" ] && continue 365107543Sscottl fi 366167917Sle ${PWCMD} usershow $_input > /dev/null 2>&1 367167917Sle if [ "$?" -eq 0 ]; then 368167917Sle err "User exists!" 369167917Sle [ -z "$fflag" ] && continue 370167917Sle fi 371107543Sscottl break 372107543Sscottl done 373107543Sscottl username="$_input" 374107543Sscottl} 375107543Sscottl 376107543Sscottl# get_gecos 377107543Sscottl# Reads extra information about the user. Can be used both in interactive 378107543Sscottl# and batch (from file) mode. 379107543Sscottl# 380107543Sscottlget_gecos() { 381107543Sscottl _input= 382107543Sscottl 383107543Sscottl # No need to take down additional user information for a configuration run. 384107543Sscottl [ -n "$configflag" ] && return 385107543Sscottl 386107543Sscottl if [ -z "$fflag" ]; then 387107543Sscottl echo -n "Full name: " 388107543Sscottl read _input 389107543Sscottl else 390107543Sscottl _input="`echo "$fileline" | cut -f7 -d:`" 391107543Sscottl fi 392107543Sscottl ugecos="$_input" 393107543Sscottl} 394107543Sscottl 395107543Sscottl# get_shell 396107543Sscottl# Get the account's shell. Works in interactive and batch mode. It 397130160Smtm# accepts either the base name of the shell or the full path. 398107543Sscottl# If an invalid shell is entered it will simply use the default shell. 399107543Sscottl# 400107543Sscottlget_shell() { 401107543Sscottl _input= 402107543Sscottl _fullpath= 403107543Sscottl ushell="$defaultshell" 404107543Sscottl 405107543Sscottl # Make sure the current value of the shell is a valid one 406127076Smtm if [ -z "$Sflag" ]; then 407127076Smtm if ! shell_exists $ushell ; then 408127076Smtm info "Using default shell ${defaultshell}." 409127076Smtm ushell="$defaultshell" 410127076Smtm fi 411116627Smtm fi 412107543Sscottl 413107543Sscottl if [ -z "$fflag" ]; then 414107543Sscottl echo -n "Shell ($shells) [`basename $ushell`]: " 415107543Sscottl read _input 416107543Sscottl else 417107543Sscottl _input="`echo "$fileline" | cut -f9 -d:`" 418107543Sscottl fi 419107543Sscottl if [ -n "$_input" ]; then 420127076Smtm if [ -n "$Sflag" ]; then 421127076Smtm ushell="$_input" 422107543Sscottl else 423127076Smtm _fullpath=`fullpath_from_shell $_input` 424127076Smtm if [ -n "$_fullpath" ]; then 425127076Smtm ushell="$_fullpath" 426127076Smtm else 427127076Smtm err "Invalid shell ($_input) for user $username." 428127076Smtm info "Using default shell ${defaultshell}." 429127076Smtm ushell="$defaultshell" 430127076Smtm fi 431107543Sscottl fi 432107543Sscottl fi 433107543Sscottl} 434107543Sscottl 435107543Sscottl# get_homedir 436107543Sscottl# Reads the account's home directory. Used both with interactive input 437107543Sscottl# and batch input. 438107543Sscottl# 439107543Sscottlget_homedir() { 440107543Sscottl _input= 441107543Sscottl if [ -z "$fflag" ]; then 442107543Sscottl echo -n "Home directory [${homeprefix}/${username}]: " 443107543Sscottl read _input 444107543Sscottl else 445107543Sscottl _input="`echo "$fileline" | cut -f8 -d:`" 446107543Sscottl fi 447107543Sscottl 448107543Sscottl if [ -n "$_input" ]; then 449107543Sscottl uhome="$_input" 450107543Sscottl # if this is a configuration run, then user input is the home 451107543Sscottl # directory prefix. Otherwise it is understood to 452107543Sscottl # be $prefix/$user 453107543Sscottl # 454107543Sscottl [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome" 455107543Sscottl else 456107543Sscottl uhome="${homeprefix}/${username}" 457107543Sscottl fi 458107543Sscottl} 459107543Sscottl 460175719Smtm# get_homeperm 461175719Smtm# Reads the account's home directory permissions. 462175719Smtm# 463175719Smtmget_homeperm() { 464175719Smtm uhomeperm=$defaultHomePerm 465175719Smtm _input= 466175719Smtm _prompt= 467175719Smtm 468175719Smtm if [ -n "$uhomeperm" ]; then 469175719Smtm _prompt="Home directory permissions [${uhomeperm}]: " 470175719Smtm else 471175719Smtm _prompt="Home directory permissions (Leave empty for default): " 472175719Smtm fi 473175719Smtm if [ -z "$fflag" ]; then 474175719Smtm echo -n "$_prompt" 475175719Smtm read _input 476175719Smtm fi 477175719Smtm 478175719Smtm if [ -n "$_input" ]; then 479175719Smtm uhomeperm="$_input" 480175719Smtm fi 481175719Smtm} 482175719Smtm 483107543Sscottl# get_uid 484107543Sscottl# Reads a numeric userid in an interactive or batch session. Automatically 485107543Sscottl# allocates one if it is not specified. 486107543Sscottl# 487107543Sscottlget_uid() { 488168656Smtm uuid=${uidstart} 489107543Sscottl _input= 490107543Sscottl _prompt= 491107543Sscottl 492107543Sscottl if [ -n "$uuid" ]; then 493232146Sdeischen uuid=`get_nextuid $uuid` 494107543Sscottl _prompt="Uid [$uuid]: " 495107543Sscottl else 496107543Sscottl _prompt="Uid (Leave empty for default): " 497107543Sscottl fi 498107543Sscottl if [ -z "$fflag" ]; then 499109573Sfjoe echo -n "$_prompt" 500107543Sscottl read _input 501107543Sscottl else 502107543Sscottl _input="`echo "$fileline" | cut -f2 -d:`" 503107543Sscottl fi 504107543Sscottl 505107543Sscottl [ -n "$_input" ] && uuid=$_input 506107543Sscottl uuid=`get_nextuid $uuid` 507107543Sscottl uidstart=$uuid 508107543Sscottl} 509107543Sscottl 510107543Sscottl# get_class 511107543Sscottl# Reads login class of account. Can be used in interactive or batch mode. 512107543Sscottl# 513107543Sscottlget_class() { 514107543Sscottl uclass="$defaultclass" 515107543Sscottl _input= 516107543Sscottl _class=${uclass:-"default"} 517107543Sscottl 518107543Sscottl if [ -z "$fflag" ]; then 519107543Sscottl echo -n "Login class [$_class]: " 520107543Sscottl read _input 521107543Sscottl else 522107543Sscottl _input="`echo "$fileline" | cut -f4 -d:`" 523107543Sscottl fi 524107543Sscottl 525107543Sscottl [ -n "$_input" ] && uclass="$_input" 526107543Sscottl} 527107543Sscottl 528107543Sscottl# get_logingroup 529107543Sscottl# Reads user's login group. Can be used in both interactive and batch 530107543Sscottl# modes. The specified value can be a group name or its numeric id. 531112433Smtm# This routine leaves the field blank if nothing is provided and 532112433Smtm# a default login group has not been set. The pw(8) command 533112433Smtm# will then provide a login group with the same name as the username. 534107543Sscottl# 535107543Sscottlget_logingroup() { 536112433Smtm ulogingroup="$defaultLgroup" 537107543Sscottl _input= 538107543Sscottl 539107543Sscottl if [ -z "$fflag" ]; then 540112433Smtm echo -n "Login group [${ulogingroup:-$username}]: " 541107543Sscottl read _input 542107543Sscottl else 543107543Sscottl _input="`echo "$fileline" | cut -f3 -d:`" 544107543Sscottl fi 545107543Sscottl 546107543Sscottl # Pw(8) will use the username as login group if it's left empty 547112433Smtm [ -n "$_input" ] && ulogingroup="$_input" 548107543Sscottl} 549107543Sscottl 550107543Sscottl# get_groups 551107543Sscottl# Read additional groups for the user. It can be used in both interactive 552107543Sscottl# and batch modes. 553107543Sscottl# 554107543Sscottlget_groups() { 555107543Sscottl ugroups="$defaultgroups" 556107543Sscottl _input= 557107543Sscottl _group=${ulogingroup:-"${username}"} 558107543Sscottl 559107543Sscottl if [ -z "$configflag" ]; then 560107543Sscottl [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username" 561107543Sscottl [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: " 562107543Sscottl else 563107543Sscottl [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: " 564107543Sscottl fi 565107543Sscottl read _input 566107543Sscottl 567107543Sscottl [ -n "$_input" ] && ugroups="$_input" 568107543Sscottl} 569107543Sscottl 570107543Sscottl# get_expire_dates 571107543Sscottl# Read expiry information for the account and also for the password. This 572107543Sscottl# routine is used only from batch processing mode. 573107543Sscottl# 574107543Sscottlget_expire_dates() { 575107543Sscottl upwexpire="`echo "$fileline" | cut -f5 -d:`" 576107543Sscottl uexpire="`echo "$fileline" | cut -f6 -d:`" 577107543Sscottl} 578107543Sscottl 579107543Sscottl# get_password 580107543Sscottl# Read the password in batch processing mode. The password field matters 581107543Sscottl# only when the password type is "yes" or "random". If the field is empty and the 582107543Sscottl# password type is "yes", then it assumes the account has an empty passsword 583107543Sscottl# and changes the password type accordingly. If the password type is "random" 584107543Sscottl# and the password field is NOT empty, then it assumes the account will NOT 585107543Sscottl# have a random password and set passwdtype to "yes." 586107543Sscottl# 587107543Sscottlget_password() { 588107543Sscottl # We may temporarily change a password type. Make sure it's changed 589107543Sscottl # back to whatever it was before we process the next account. 590107543Sscottl # 591107543Sscottl [ -n "$savedpwtype" ] && { 592107543Sscottl passwdtype=$savedpwtype 593107543Sscottl savedpwtype= 594107543Sscottl } 595107543Sscottl 596107543Sscottl # There may be a ':' in the password 597107543Sscottl upass=${fileline#*:*:*:*:*:*:*:*:*:} 598107543Sscottl 599107543Sscottl if [ -z "$upass" ]; then 600107543Sscottl case $passwdtype in 601107543Sscottl yes) 602107543Sscottl # if it's empty, assume an empty password 603107543Sscottl passwdtype=none 604107543Sscottl savedpwtype=yes 605107543Sscottl ;; 606107543Sscottl esac 607107543Sscottl else 608107543Sscottl case $passwdtype in 609107543Sscottl random) 610107543Sscottl passwdtype=yes 611107543Sscottl savedpwtype=random 612107543Sscottl ;; 613107543Sscottl esac 614107543Sscottl fi 615107543Sscottl} 616107543Sscottl 617107543Sscottl# input_from_file 618107543Sscottl# Reads a line of account information from standard input and 619107543Sscottl# adds it to the user database. 620107543Sscottl# 621107543Sscottlinput_from_file() { 622107543Sscottl _field= 623107543Sscottl 624110595Smtm while read -r fileline ; do 625107543Sscottl case "$fileline" in 626107543Sscottl \#*|'') 627107543Sscottl ;; 628168651Smtm *) 629168651Smtm get_user || continue 630168651Smtm get_gecos 631168651Smtm get_uid 632168651Smtm get_logingroup 633168651Smtm get_class 634168651Smtm get_shell 635168651Smtm get_homedir 636175719Smtm get_homeperm 637168651Smtm get_password 638168651Smtm get_expire_dates 639172823Smtm ugroups="$defaultgroups" 640168651Smtm 641168651Smtm add_user 642168651Smtm ;; 643107543Sscottl esac 644107543Sscottl done 645107543Sscottl} 646107543Sscottl 647107543Sscottl# input_interactive 648107543Sscottl# Prompts for user information interactively, and commits to 649107543Sscottl# the user database. 650107543Sscottl# 651107543Sscottlinput_interactive() { 652107543Sscottl _disable= 653107543Sscottl _pass= 654107543Sscottl _passconfirm= 655107543Sscottl _random="no" 656107543Sscottl _emptypass="no" 657107543Sscottl _usepass="yes" 658112401Smtm _logingroup_ok="no" 659112401Smtm _groups_ok="no" 660107543Sscottl case $passwdtype in 661107543Sscottl none) 662107543Sscottl _emptypass="yes" 663107543Sscottl _usepass="yes" 664107543Sscottl ;; 665107543Sscottl no) 666107543Sscottl _usepass="no" 667107543Sscottl ;; 668107543Sscottl random) 669107543Sscottl _random="yes" 670107543Sscottl ;; 671107543Sscottl esac 672107543Sscottl 673107543Sscottl get_user 674107543Sscottl get_gecos 675107543Sscottl get_uid 676110537Sadrian 677110537Sadrian # The case where group = user is handled elsewhere, so 678110537Sadrian # validate any other groups the user is invited to. 679110537Sadrian until [ "$_logingroup_ok" = yes ]; do 680110537Sadrian get_logingroup 681110537Sadrian _logingroup_ok=yes 682110537Sadrian if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then 683110537Sadrian if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then 684110537Sadrian echo "Group $ulogingroup does not exist!" 685110537Sadrian _logingroup_ok=no 686110537Sadrian fi 687110537Sadrian fi 688110537Sadrian done 689110537Sadrian until [ "$_groups_ok" = yes ]; do 690110537Sadrian get_groups 691110537Sadrian _groups_ok=yes 692110537Sadrian for i in $ugroups; do 693110537Sadrian if [ "$username" != "$i" ]; then 694110537Sadrian if ! ${PWCMD} show group $i > /dev/null 2>&1; then 695110537Sadrian echo "Group $i does not exist!" 696110537Sadrian _groups_ok=no 697110537Sadrian fi 698110537Sadrian fi 699110537Sadrian done 700110537Sadrian done 701110537Sadrian 702107543Sscottl get_class 703107543Sscottl get_shell 704107543Sscottl get_homedir 705175719Smtm get_homeperm 706107543Sscottl 707107543Sscottl while : ; do 708107543Sscottl echo -n "Use password-based authentication? [$_usepass]: " 709107543Sscottl read _input 710107543Sscottl [ -z "$_input" ] && _input=$_usepass 711107543Sscottl case $_input in 712107543Sscottl [Nn][Oo]|[Nn]) 713107543Sscottl passwdtype="no" 714107543Sscottl ;; 715107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 716107543Sscottl while : ; do 717107543Sscottl echo -n "Use an empty password? (yes/no) [$_emptypass]: " 718107543Sscottl read _input 719107543Sscottl [ -n "$_input" ] && _emptypass=$_input 720107543Sscottl case $_emptypass in 721107543Sscottl [Nn][Oo]|[Nn]) 722107543Sscottl echo -n "Use a random password? (yes/no) [$_random]: " 723107543Sscottl read _input 724107543Sscottl [ -n "$_input" ] && _random="$_input" 725107543Sscottl case $_random in 726107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 727107543Sscottl passwdtype="random" 728107543Sscottl break 729107543Sscottl ;; 730107543Sscottl esac 731107543Sscottl passwdtype="yes" 732112401Smtm [ -n "$configflag" ] && break 733107543Sscottl trap 'stty echo; exit' 0 1 2 3 15 734107543Sscottl stty -echo 735107543Sscottl echo -n "Enter password: " 736360390Skevans IFS= read -r upass 737107543Sscottl echo'' 738107543Sscottl echo -n "Enter password again: " 739360390Skevans IFS= read -r _passconfirm 740107543Sscottl echo '' 741107543Sscottl stty echo 742107543Sscottl # if user entered a blank password 743107543Sscottl # explicitly ask again. 744107543Sscottl [ -z "$upass" -a -z "$_passconfirm" ] \ 745107543Sscottl && continue 746107543Sscottl ;; 747107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 748107543Sscottl passwdtype="none" 749107543Sscottl break; 750107543Sscottl ;; 751107543Sscottl *) 752107543Sscottl # invalid answer; repeat the loop 753107543Sscottl continue 754107543Sscottl ;; 755107543Sscottl esac 756107543Sscottl if [ "$upass" != "$_passconfirm" ]; then 757107543Sscottl echo "Passwords did not match!" 758107543Sscottl continue 759107543Sscottl fi 760107543Sscottl break 761107543Sscottl done 762107543Sscottl ;; 763107543Sscottl *) 764107543Sscottl # invalid answer; repeat loop 765107543Sscottl continue 766107543Sscottl ;; 767107543Sscottl esac 768107543Sscottl break; 769107543Sscottl done 770107543Sscottl _disable=${disableflag:-"no"} 771107543Sscottl while : ; do 772107543Sscottl echo -n "Lock out the account after creation? [$_disable]: " 773107543Sscottl read _input 774107543Sscottl [ -z "$_input" ] && _input=$_disable 775107543Sscottl case $_input in 776107543Sscottl [Nn][Oo]|[Nn]) 777107543Sscottl disableflag= 778107543Sscottl ;; 779107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 780107543Sscottl disableflag=yes 781107543Sscottl ;; 782107543Sscottl *) 783107543Sscottl # invalid answer; repeat loop 784107543Sscottl continue 785107543Sscottl ;; 786107543Sscottl esac 787107543Sscottl break 788107543Sscottl done 789107543Sscottl 790107543Sscottl # Display the information we have so far and prompt to 791107543Sscottl # commit it. 792107543Sscottl # 793107543Sscottl _disable=${disableflag:-"no"} 794107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username 795107543Sscottl case $passwdtype in 796107543Sscottl yes) 797107543Sscottl _pass='*****' 798107543Sscottl ;; 799107543Sscottl no) 800107543Sscottl _pass='<disabled>' 801107543Sscottl ;; 802107543Sscottl none) 803107543Sscottl _pass='<blank>' 804107543Sscottl ;; 805107543Sscottl random) 806107543Sscottl _pass='<random>' 807107543Sscottl ;; 808107543Sscottl esac 809109751Sfjoe [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass" 810109751Sfjoe [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype" 811107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos" 812107543Sscottl [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid" 813107543Sscottl printf "%-10s : %s\n" "Class" "$uclass" 814112433Smtm printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups" 815107543Sscottl printf "%-10s : %s\n" "Home" "$uhome" 816175719Smtm printf "%-10s : %s\n" "Home Mode" "$uhomeperm" 817107543Sscottl printf "%-10s : %s\n" "Shell" "$ushell" 818107543Sscottl printf "%-10s : %s\n" "Locked" "$_disable" 819107543Sscottl while : ; do 820107543Sscottl echo -n "OK? (yes/no): " 821107543Sscottl read _input 822107543Sscottl case $_input in 823107543Sscottl [Nn][Oo]|[Nn]) 824107543Sscottl return 1 825107543Sscottl ;; 826107543Sscottl [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 827107543Sscottl add_user 828107543Sscottl ;; 829107543Sscottl *) 830107543Sscottl continue 831107543Sscottl ;; 832107543Sscottl esac 833107543Sscottl break 834107543Sscottl done 835107543Sscottl return 0 836107543Sscottl} 837107543Sscottl 838145618Srobert#### END SUBROUTINE DEFINITION #### 839107543Sscottl 840107543SscottlTHISCMD=`/usr/bin/basename $0` 841107543SscottlDEFAULTSHELL=/bin/sh 842107543SscottlADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}" 843107543SscottlPWCMD="${PWCMD:-/usr/sbin/pw}" 844107543SscottlMAILCMD="${MAILCMD:-mail}" 845107543SscottlETCSHELLS="${ETCSHELLS:-/etc/shells}" 846127076SmtmNOHOME="/nonexistent" 847116627SmtmNOLOGIN="nologin" 848127635ScpercivaNOLOGIN_PATH="/usr/sbin/nologin" 849109751SfjoeGREPCMD="/usr/bin/grep" 850109751SfjoeDATECMD="/bin/date" 851107543Sscottl 852107543Sscottl# Set default values 853107543Sscottl# 854107543Sscottlusername= 855107543Sscottluuid= 856107543Sscottluidstart= 857107543Sscottlugecos= 858107543Sscottlulogingroup= 859107543Sscottluclass= 860107543Sscottluhome= 861175719Smtmuhomeperm= 862107543Sscottlupass= 863107543Sscottlushell= 864107543Sscottludotdir=/usr/share/skel 865107543Sscottlugroups= 866107543Sscottluexpire= 867107543Sscottlupwexpire= 868107543Sscottlshells="`valid_shells`" 869107543Sscottlpasswdtype="yes" 870107543Sscottlmsgfile=/etc/adduser.msg 871107543Sscottlmsgflag= 872107543Sscottlquietflag= 873107543Sscottlconfigflag= 874107543Sscottlfflag= 875107543Sscottlinfile= 876107543Sscottldisableflag= 877127076SmtmDflag= 878127076SmtmSflag= 879107543Sscottlreadconfig="yes" 880107543Sscottlhomeprefix="/home" 881107543Sscottlrandompass= 882107543Sscottlfileline= 883107543Sscottlsavedpwtype= 884107543Sscottldefaultclass= 885112433SmtmdefaultLgroup= 886116784Smtmdefaultgroups= 887107543Sscottldefaultshell="${DEFAULTSHELL}" 888175719SmtmdefaultHomePerm= 889107543Sscottl 890107543Sscottl# Make sure the user running this program is root. This isn't a security 891202312Sjoel# measure as much as it is a useful method of reminding the user to 892107543Sscottl# 'su -' before he/she wastes time entering data that won't be saved. 893107543Sscottl# 894107543Sscottlprocowner=${procowner:-`/usr/bin/id -u`} 895107543Sscottlif [ "$procowner" != "0" ]; then 896107543Sscottl err 'you must be the super-user (uid 0) to use this utility.' 897107543Sscottl exit 1 898107543Sscottlfi 899107543Sscottl 900228990Suqs# Override from our conf file 901107543Sscottl# Quickly go through the commandline line to see if we should read 902107543Sscottl# from our configuration file. The actual parsing of the commandline 903107543Sscottl# arguments happens after we read in our configuration file (commandline 904107543Sscottl# should override configuration file). 905107543Sscottl# 906107543Sscottlfor _i in $* ; do 907107543Sscottl if [ "$_i" = "-N" ]; then 908107543Sscottl readconfig= 909107543Sscottl break; 910107543Sscottl fi 911107543Sscottldone 912107543Sscottlif [ -n "$readconfig" ]; then 913107543Sscottl # On a long-lived system, the first time this script is run it 914107543Sscottl # will barf upon reading the configuration file for its perl predecessor. 915107543Sscottl if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then 916107543Sscottl [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1 917107543Sscottl fi 918107543Sscottlfi 919107543Sscottl 920228990Suqs# Process command-line options 921107543Sscottl# 922107543Sscottlfor _switch ; do 923107543Sscottl case $_switch in 924107543Sscottl -L) 925107543Sscottl defaultclass="$2" 926107543Sscottl shift; shift 927107543Sscottl ;; 928107543Sscottl -C) 929107543Sscottl configflag=yes 930107543Sscottl shift 931107543Sscottl ;; 932127076Smtm -D) 933127076Smtm Dflag=yes 934127076Smtm shift 935127076Smtm ;; 936107543Sscottl -E) 937107543Sscottl disableflag=yes 938107543Sscottl shift 939107543Sscottl ;; 940107543Sscottl -k) 941107543Sscottl udotdir="$2" 942107543Sscottl shift; shift 943107543Sscottl ;; 944107543Sscottl -f) 945107543Sscottl [ "$2" != "-" ] && infile="$2" 946107543Sscottl fflag=yes 947107543Sscottl shift; shift 948107543Sscottl ;; 949112433Smtm -g) 950112433Smtm defaultLgroup="$2" 951112433Smtm shift; shift 952112433Smtm ;; 953107543Sscottl -G) 954107543Sscottl defaultgroups="$2" 955107543Sscottl shift; shift 956107543Sscottl ;; 957107543Sscottl -h) 958107543Sscottl show_usage 959107543Sscottl exit 0 960107543Sscottl ;; 961107543Sscottl -d) 962107543Sscottl homeprefix="$2" 963107543Sscottl shift; shift 964107543Sscottl ;; 965107543Sscottl -m) 966107543Sscottl case "$2" in 967107543Sscottl [Nn][Oo]) 968107543Sscottl msgflag= 969107543Sscottl ;; 970107543Sscottl *) 971107543Sscottl msgflag=yes 972107543Sscottl msgfile="$2" 973107543Sscottl ;; 974107543Sscottl esac 975107543Sscottl shift; shift 976107543Sscottl ;; 977175719Smtm -M) 978175719Smtm defaultHomePerm=$2 979175719Smtm shift; shift 980175719Smtm ;; 981107543Sscottl -N) 982107543Sscottl readconfig= 983107543Sscottl shift 984107543Sscottl ;; 985107543Sscottl -w) 986107543Sscottl case "$2" in 987107543Sscottl no|none|random|yes) 988107543Sscottl passwdtype=$2 989107543Sscottl ;; 990107543Sscottl *) 991107543Sscottl show_usage 992107543Sscottl exit 1 993107543Sscottl ;; 994107543Sscottl esac 995107543Sscottl shift; shift 996107543Sscottl ;; 997107543Sscottl -q) 998107543Sscottl quietflag=yes 999107543Sscottl shift 1000107543Sscottl ;; 1001107543Sscottl -s) 1002107543Sscottl defaultshell="`fullpath_from_shell $2`" 1003107543Sscottl shift; shift 1004107543Sscottl ;; 1005127076Smtm -S) 1006127076Smtm Sflag=yes 1007127076Smtm shift 1008127076Smtm ;; 1009107543Sscottl -u) 1010107543Sscottl uidstart=$2 1011107543Sscottl shift; shift 1012107543Sscottl ;; 1013107543Sscottl esac 1014107543Sscottldone 1015107543Sscottl 1016107543Sscottl# If the -f switch was used, get input from a file. Otherwise, 1017107543Sscottl# this is an interactive session. 1018107543Sscottl# 1019107543Sscottlif [ -n "$fflag" ]; then 1020107543Sscottl if [ -z "$infile" ]; then 1021107543Sscottl input_from_file 1022107543Sscottl elif [ -n "$infile" ]; then 1023107543Sscottl if [ -r "$infile" ]; then 1024107543Sscottl input_from_file < $infile 1025107543Sscottl else 1026107543Sscottl err "File ($infile) is unreadable or does not exist." 1027107543Sscottl fi 1028107543Sscottl fi 1029107543Sscottlelse 1030107543Sscottl input_interactive 1031109768Smtm while : ; do 1032112401Smtm if [ -z "$configflag" ]; then 1033112401Smtm echo -n "Add another user? (yes/no): " 1034112401Smtm else 1035112401Smtm echo -n "Re-edit the default configuration? (yes/no): " 1036112401Smtm fi 1037109768Smtm read _input 1038109768Smtm case $_input in 1039109768Smtm [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 1040109768Smtm uidstart=`get_nextuid $uidstart` 1041109768Smtm input_interactive 1042109768Smtm continue 1043109768Smtm ;; 1044109768Smtm [Nn][Oo]|[Nn]) 1045109768Smtm echo "Goodbye!" 1046109768Smtm ;; 1047109768Smtm *) 1048109768Smtm continue 1049109768Smtm ;; 1050109768Smtm esac 1051109768Smtm break 1052109768Smtm done 1053107543Sscottlfi 1054