17cdfce09SScott Long#!/bin/sh 27cdfce09SScott Long# 34d846d26SWarner Losh# SPDX-License-Identifier: BSD-2-Clause 41de7b4b8SPedro F. Giffuni# 50d640c0dSMike Makonnen# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved. 67cdfce09SScott Long# 77cdfce09SScott Long# Redistribution and use in source and binary forms, with or without 87cdfce09SScott Long# modification, are permitted provided that the following conditions 97cdfce09SScott Long# are met: 107cdfce09SScott Long# 1. Redistributions of source code must retain the above copyright 117cdfce09SScott Long# notice, this list of conditions and the following disclaimer. 127cdfce09SScott Long# 2. Redistributions in binary form must reproduce the above copyright 137cdfce09SScott Long# notice, this list of conditions and the following disclaimer in the 147cdfce09SScott Long# documentation and/or other materials provided with the distribution. 157cdfce09SScott Long# 167cdfce09SScott Long# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 177cdfce09SScott Long# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 187cdfce09SScott Long# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 197cdfce09SScott Long# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 207cdfce09SScott Long# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 217cdfce09SScott Long# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 227cdfce09SScott Long# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 237cdfce09SScott Long# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 247cdfce09SScott Long# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 257cdfce09SScott Long# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 267cdfce09SScott Long# 2722884fddSMike Makonnen# Email: Mike Makonnen <mtm@FreeBSD.Org> 287cdfce09SScott Long# 297cdfce09SScott Long# 307cdfce09SScott Long 317cdfce09SScott Long# err msg 327cdfce09SScott Long# Display $msg on stderr, unless we're being quiet. 337cdfce09SScott Long# 347cdfce09SScott Longerr() { 357cdfce09SScott Long if [ -z "$quietflag" ]; then 367cdfce09SScott Long echo 1>&2 ${THISCMD}: ERROR: $* 377cdfce09SScott Long fi 387cdfce09SScott Long} 397cdfce09SScott Long 407cdfce09SScott Long# info msg 417cdfce09SScott Long# Display $msg on stdout, unless we're being quiet. 427cdfce09SScott Long# 437cdfce09SScott Longinfo() { 447cdfce09SScott Long if [ -z "$quietflag" ]; then 457cdfce09SScott Long echo ${THISCMD}: INFO: $* 467cdfce09SScott Long fi 477cdfce09SScott Long} 487cdfce09SScott Long 497cdfce09SScott Long# get_nextuid 507cdfce09SScott Long# Output the value of $_uid if it is available for use. If it 517cdfce09SScott Long# is not, output the value of the next higher uid that is available. 527cdfce09SScott Long# If a uid is not specified, output the first available uid, as indicated 537cdfce09SScott Long# by pw(8). 547cdfce09SScott Long# 557cdfce09SScott Longget_nextuid () { 5646a619c6SDag-Erling Smørgrav local _uid=$1 _nextuid= 577cdfce09SScott Long 587cdfce09SScott Long if [ -z "$_uid" ]; then 59170d0882SDag-Erling Smørgrav _nextuid="$(${PWCMD} usernext | cut -f1 -d:)" 607cdfce09SScott Long else 617cdfce09SScott Long while : ; do 62b3733389SDag-Erling Smørgrav if ! ${PWCMD} usershow $_uid > /dev/null 2>&1; then 637cdfce09SScott Long _nextuid=$_uid 647cdfce09SScott Long break 657cdfce09SScott Long fi 66b3733389SDag-Erling Smørgrav _uid=$((_uid + 1)) 677cdfce09SScott Long done 687cdfce09SScott Long fi 697cdfce09SScott Long echo $_nextuid 707cdfce09SScott Long} 717cdfce09SScott Long 727cdfce09SScott Long# show_usage 737cdfce09SScott Long# Display usage information for this utility. 747cdfce09SScott Long# 757cdfce09SScott Longshow_usage() { 767cdfce09SScott Long echo "usage: ${THISCMD} [options]" 777cdfce09SScott Long echo " options may include:" 787cdfce09SScott Long echo " -C save to the configuration file only" 790d640c0dSMike Makonnen echo " -D do not attempt to create the home directory" 807cdfce09SScott Long echo " -E disable this account after creation" 817cdfce09SScott Long echo " -G additional groups to add accounts to" 827cdfce09SScott Long echo " -L login class of the user" 8343cb08ceSMike Makonnen echo " -M file permission for home directory" 847cdfce09SScott Long echo " -N do not read configuration file" 85215c0a51SJohn Grafton echo " -Z do not attempt to create ZFS home dataset" 860d640c0dSMike Makonnen echo " -S a nonexistent shell is not an error" 877cdfce09SScott Long echo " -d home directory" 887cdfce09SScott Long echo " -f file from which input will be received" 89e33b8d97SMike Makonnen echo " -g default login group" 907cdfce09SScott Long echo " -h display this usage message" 917cdfce09SScott Long echo " -k path to skeleton home directory" 927cdfce09SScott Long echo " -m user welcome message file" 937cdfce09SScott Long echo " -q absolute minimal user feedback" 947cdfce09SScott Long echo " -s shell" 957cdfce09SScott Long echo " -u uid to start at" 967cdfce09SScott Long echo " -w password type: no, none, yes or random" 977cdfce09SScott Long} 987cdfce09SScott Long 997cdfce09SScott Long# valid_shells 1007cdfce09SScott Long# Outputs a list of valid shells from /etc/shells. Only the 1017cdfce09SScott Long# basename of the shell is output. 1027cdfce09SScott Long# 1037cdfce09SScott Longvalid_shells() { 10446a619c6SDag-Erling Smørgrav local _prefix= 105170d0882SDag-Erling Smørgrav 106170d0882SDag-Erling Smørgrav ${GREPCMD} '^[^#]' ${ETCSHELLS} | 1077cdfce09SScott Long while read _path _junk ; do 108170d0882SDag-Erling Smørgrav echo -n "${_prefix}${_path##*/}" 1097cdfce09SScott Long _prefix=' ' 1107cdfce09SScott Long done 1118923e98bSMike Makonnen 11268050033SColin Percival # /usr/sbin/nologin is a special case 1138923e98bSMike Makonnen [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}" 1147cdfce09SScott Long} 1157cdfce09SScott Long 1167cdfce09SScott Long# fullpath_from_shell shell 117e7291a6bSMike Makonnen# Given $shell, which is either the full path to a shell or 118e7291a6bSMike Makonnen# the basename component of a valid shell, get the 1197cdfce09SScott Long# full path to the shell from the /etc/shells file. 1207cdfce09SScott Long# 1217cdfce09SScott Longfullpath_from_shell() { 12246a619c6SDag-Erling Smørgrav local _shell=$1 _fullpath= 123170d0882SDag-Erling Smørgrav 124170d0882SDag-Erling Smørgrav if [ -z "$_shell" ]; then 125170d0882SDag-Erling Smørgrav return 126170d0882SDag-Erling Smørgrav fi 1277cdfce09SScott Long 128ce655e04SPeter Pentchev # /usr/sbin/nologin is a special case; it needs to be handled 129170d0882SDag-Erling Smørgrav # before the grep | while loop, since a 'return' from within 130ce655e04SPeter Pentchev # a subshell will not terminate the function's execution, and 131ce655e04SPeter Pentchev # the path to the nologin shell might be printed out twice. 132ce655e04SPeter Pentchev # 133170d0882SDag-Erling Smørgrav if [ "$_shell" = "${NOLOGIN}" ] || 134170d0882SDag-Erling Smørgrav [ "$_shell" = "${NOLOGIN_PATH}" ]; then 135ce655e04SPeter Pentchev echo ${NOLOGIN_PATH} 136170d0882SDag-Erling Smørgrav return 137ce655e04SPeter Pentchev fi 138ce655e04SPeter Pentchev 139170d0882SDag-Erling Smørgrav ${GREPCMD} '^[^#]' ${ETCSHELLS} | 1407cdfce09SScott Long while read _path _junk ; do 141170d0882SDag-Erling Smørgrav if [ "$_path" = "$_shell" ] || 142170d0882SDag-Erling Smørgrav [ "${_path##*/}" = "$_shell" ]; then 143170d0882SDag-Erling Smørgrav echo "$_path" 144170d0882SDag-Erling Smørgrav break 1457cdfce09SScott Long fi 1467cdfce09SScott Long done 1477cdfce09SScott Long} 1487cdfce09SScott Long 1498923e98bSMike Makonnen# shell_exists shell 1508923e98bSMike Makonnen# If the given shell is listed in ${ETCSHELLS} or it is 1518923e98bSMike Makonnen# the nologin shell this function will return 0. 1528923e98bSMike Makonnen# Otherwise, it will return 1. If shell is valid but 1538923e98bSMike Makonnen# the path is invalid or it is not executable it 1548923e98bSMike Makonnen# will emit an informational message saying so. 1558923e98bSMike Makonnen# 15605c9bdf5SJoel Dahlshell_exists() { 15746a619c6SDag-Erling Smørgrav local _sh=$1 1588923e98bSMike Makonnen 159170d0882SDag-Erling Smørgrav if [ -z "$(fullpath_from_shell "$_sh")" ] ; then 1608923e98bSMike Makonnen err "Invalid shell ($_sh) for user $username." 1618923e98bSMike Makonnen return 1 1628923e98bSMike Makonnen fi 163170d0882SDag-Erling Smørgrav [ -x "$_sh" ] || 1640e08168eSMike Makonnen info "The shell ($_sh) does not exist or is not executable." 1658923e98bSMike Makonnen return 0 1668923e98bSMike Makonnen} 1678923e98bSMike Makonnen 1687cdfce09SScott Long# save_config 1697cdfce09SScott Long# Save some variables to a configuration file. 1707cdfce09SScott Long# Note: not all script variables are saved, only those that 1717cdfce09SScott Long# it makes sense to save. 1727cdfce09SScott Long# 1737cdfce09SScott Longsave_config() { 1747cdfce09SScott Long echo "# Configuration file for adduser(8)." > ${ADDUSERCONF} 1757cdfce09SScott Long echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF} 176170d0882SDag-Erling Smørgrav echo "# Last Modified on $(${DATECMD})." >> ${ADDUSERCONF} 1777cdfce09SScott Long echo '' >> ${ADDUSERCONF} 17843cb08ceSMike Makonnen echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF} 179642a7af5SMike Makonnen echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF} 1807cdfce09SScott Long echo "defaultclass=$uclass" >> ${ADDUSERCONF} 1817cdfce09SScott Long echo "defaultgroups=$ugroups" >> ${ADDUSERCONF} 1827cdfce09SScott Long echo "passwdtype=$passwdtype" >> ${ADDUSERCONF} 1837cdfce09SScott Long echo "homeprefix=$homeprefix" >> ${ADDUSERCONF} 1847cdfce09SScott Long echo "defaultshell=$ushell" >> ${ADDUSERCONF} 1857cdfce09SScott Long echo "udotdir=$udotdir" >> ${ADDUSERCONF} 1867cdfce09SScott Long echo "msgfile=$msgfile" >> ${ADDUSERCONF} 1877cdfce09SScott Long echo "disableflag=$disableflag" >> ${ADDUSERCONF} 188ac3cd471SMike Makonnen echo "uidstart=$uidstart" >> ${ADDUSERCONF} 1897cdfce09SScott Long} 1907cdfce09SScott Long 1917cdfce09SScott Long# add_user 1927cdfce09SScott Long# Add a user to the user database. If the user chose to send a welcome 1937cdfce09SScott Long# message or lock the account, do so. 1947cdfce09SScott Long# 1957cdfce09SScott Longadd_user() { 19646a619c6SDag-Erling Smørgrav local _uid= _name= _comment= _gecos= _home= _group= _grouplist= 19746a619c6SDag-Erling Smørgrav local _shell= _class= _dotdir= _expire= _pwexpire= _passwd= _upasswd= 19846a619c6SDag-Erling Smørgrav local _passwdmethod= _pwcmd= 1997cdfce09SScott Long 2007cdfce09SScott Long # Is this a configuration run? If so, don't modify user database. 2017cdfce09SScott Long # 2027cdfce09SScott Long if [ -n "$configflag" ]; then 2037cdfce09SScott Long save_config 2047cdfce09SScott Long return 2057cdfce09SScott Long fi 2067cdfce09SScott Long 207c8336599SMike Makonnen _name="-n '$username'" 208c95c14d4SMike Makonnen [ -n "$uuid" ] && _uid='-u "$uuid"' 209c95c14d4SMike Makonnen [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"' 210c95c14d4SMike Makonnen [ -n "$ugroups" ] && _grouplist='-G "$ugroups"' 211c95c14d4SMike Makonnen [ -n "$ushell" ] && _shell='-s "$ushell"' 212c95c14d4SMike Makonnen [ -n "$uclass" ] && _class='-L "$uclass"' 213c95c14d4SMike Makonnen [ -n "$ugecos" ] && _comment='-c "$ugecos"' 214c95c14d4SMike Makonnen [ -n "$udotdir" ] && _dotdir='-k "$udotdir"' 215c95c14d4SMike Makonnen [ -n "$uexpire" ] && _expire='-e "$uexpire"' 216c95c14d4SMike Makonnen [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"' 217170d0882SDag-Erling Smørgrav if [ -z "$Dflag" ] && [ -n "$uhome" ]; then 2180d640c0dSMike Makonnen # The /nonexistent home directory is special. It 2190d640c0dSMike Makonnen # means the user has no home directory. 2200d640c0dSMike Makonnen if [ "$uhome" = "$NOHOME" ]; then 2210d640c0dSMike Makonnen _home='-d "$uhome"' 2220d640c0dSMike Makonnen else 22343cb08ceSMike Makonnen # Use home directory permissions if specified 22443cb08ceSMike Makonnen if [ -n "$uhomeperm" ]; then 22543cb08ceSMike Makonnen _home='-m -d "$uhome" -M "$uhomeperm"' 22643cb08ceSMike Makonnen else 2270d640c0dSMike Makonnen _home='-m -d "$uhome"' 2280d640c0dSMike Makonnen fi 22943cb08ceSMike Makonnen fi 230170d0882SDag-Erling Smørgrav elif [ -n "$Dflag" ] && [ -n "$uhome" ]; then 2310d640c0dSMike Makonnen _home='-d "$uhome"' 2320d640c0dSMike Makonnen fi 2337cdfce09SScott Long case $passwdtype in 2347cdfce09SScott Long no) 2357cdfce09SScott Long _passwdmethod="-w no" 2367cdfce09SScott Long _passwd="-h -" 2377cdfce09SScott Long ;; 2387cdfce09SScott Long yes) 239c95c14d4SMike Makonnen # Note on processing the password: The outer double quotes 240c95c14d4SMike Makonnen # make literal everything except ` and \ and $. 241c95c14d4SMike Makonnen # The outer single quotes make literal ` and $. 242c95c14d4SMike Makonnen # We can ensure the \ isn't treated specially by specifying 243c95c14d4SMike Makonnen # the -r switch to the read command used to obtain the input. 244c95c14d4SMike Makonnen # 2457cdfce09SScott Long _passwdmethod="-w yes" 2467cdfce09SScott Long _passwd="-h 0" 247c95c14d4SMike Makonnen _upasswd='echo "$upass" |' 2487cdfce09SScott Long ;; 2497cdfce09SScott Long none) 2507cdfce09SScott Long _passwdmethod="-w none" 2517cdfce09SScott Long ;; 2527cdfce09SScott Long random) 2537cdfce09SScott Long _passwdmethod="-w random" 2547cdfce09SScott Long ;; 2557cdfce09SScott Long esac 2567cdfce09SScott Long 257215c0a51SJohn Grafton # create ZFS dataset before home directory is created with pw 258215c0a51SJohn Grafton if [ "${Zcreate}" = "yes" ]; then 259215c0a51SJohn Grafton if [ "${Zencrypt}" = "yes" ]; then 260215c0a51SJohn Grafton echo "Enter encryption keyphrase for ZFS dataset (${zhome}):" 261215c0a51SJohn Grafton fi 262215c0a51SJohn Grafton if [ -n "$BSDINSTALL_CHROOT" ]; then 263215c0a51SJohn Grafton create_zfs_chrooted_dataset 264215c0a51SJohn Grafton else 265b3733389SDag-Erling Smørgrav if ! create_zfs_dataset; then 266215c0a51SJohn Grafton err "There was an error adding user ($username)." 267215c0a51SJohn Grafton return 1 268215c0a51SJohn Grafton fi 269215c0a51SJohn Grafton fi 270215c0a51SJohn Grafton fi 271215c0a51SJohn Grafton 2727cdfce09SScott Long _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment" 2737cdfce09SScott Long _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd" 2747cdfce09SScott Long _pwcmd="$_pwcmd $_expire $_pwexpire" 2757cdfce09SScott Long 276170d0882SDag-Erling Smørgrav if ! _output=$(eval $_pwcmd) ; then 2777cdfce09SScott Long err "There was an error adding user ($username)." 2787cdfce09SScott Long return 1 2797cdfce09SScott Long else 2807cdfce09SScott Long info "Successfully added ($username) to the user database." 2817cdfce09SScott Long if [ "random" = "$passwdtype" ]; then 2827cdfce09SScott Long randompass="$_output" 2837cdfce09SScott Long info "Password for ($username) is: $randompass" 2847cdfce09SScott Long fi 2857cdfce09SScott Long fi 2867cdfce09SScott Long 2877cdfce09SScott Long if [ -n "$disableflag" ]; then 2887cdfce09SScott Long if ${PWCMD} lock $username ; then 2897cdfce09SScott Long info "Account ($username) is locked." 2907cdfce09SScott Long else 2917cdfce09SScott Long info "Account ($username) could NOT be locked." 2927cdfce09SScott Long fi 2937cdfce09SScott Long fi 2947cdfce09SScott Long 295215c0a51SJohn Grafton # give newly created user permissions to their home zfs dataset 296215c0a51SJohn Grafton if [ "${Zcreate}" = "yes" ]; then 297215c0a51SJohn Grafton set_zfs_perms 298215c0a51SJohn Grafton if [ -n "$BSDINSTALL_CHROOT" ]; then 299215c0a51SJohn Grafton umount_legacy_zfs 300215c0a51SJohn Grafton fi 301215c0a51SJohn Grafton fi 302215c0a51SJohn Grafton 30346a619c6SDag-Erling Smørgrav local _line= _owner= _perms= _file= _dir= 3047cdfce09SScott Long if [ -n "$msgflag" ]; then 305170d0882SDag-Erling Smørgrav if [ -r "$msgfile" ]; then 3067cdfce09SScott Long # We're evaluating the contents of an external file. 3077cdfce09SScott Long # Let's not open ourselves up for attack. _perms will 3087cdfce09SScott Long # be empty if it's writeable only by the owner. _owner 3097cdfce09SScott Long # will *NOT* be empty if the file is owned by root. 3107cdfce09SScott Long # 311170d0882SDag-Erling Smørgrav _dir="$(dirname "$msgfile")" 312170d0882SDag-Erling Smørgrav _file="$(basename "$msgfile")" 313170d0882SDag-Erling Smørgrav _perms=$(/usr/bin/find "$_dir" -name "$_file" -perm +07022 -prune) 314170d0882SDag-Erling Smørgrav _owner=$(/usr/bin/find "$_dir" -name "$_file" -user 0 -prune) 315170d0882SDag-Erling Smørgrav if [ -z "$_owner" ] || [ -n "$_perms" ]; then 3167cdfce09SScott Long err "The message file ($msgfile) may be writeable only by root." 3177cdfce09SScott Long return 1 3187cdfce09SScott Long fi 3197cdfce09SScott Long while read _line ; do 3207cdfce09SScott Long eval echo "$_line" 321170d0882SDag-Erling Smørgrav done <"$msgfile" | ${MAILCMD} -s"Welcome" ${username} 3227cdfce09SScott Long info "Sent welcome message to ($username)." 323170d0882SDag-Erling Smørgrav fi 3247cdfce09SScott Long fi 3257cdfce09SScott Long} 3267cdfce09SScott Long 3277cdfce09SScott Long# get_user 3287cdfce09SScott Long# Reads username of the account from standard input or from a global 3297cdfce09SScott Long# variable containing an account line from a file. The username is 3307cdfce09SScott Long# required. If this is an interactive session it will prompt in 3317cdfce09SScott Long# a loop until a username is entered. If it is batch processing from 3327cdfce09SScott Long# a file it will output an error message and return to the caller. 3337cdfce09SScott Long# 3347cdfce09SScott Longget_user() { 33546a619c6SDag-Erling Smørgrav local _input= 3367cdfce09SScott Long 3377cdfce09SScott Long # No need to take down user names if this is a configuration saving run. 3387cdfce09SScott Long [ -n "$configflag" ] && return 3397cdfce09SScott Long 3407cdfce09SScott Long while : ; do 3417cdfce09SScott Long if [ -z "$fflag" ]; then 3427cdfce09SScott Long echo -n "Username: " 3437cdfce09SScott Long read _input 3447cdfce09SScott Long else 345170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f1 -d:)" 3467cdfce09SScott Long fi 3477cdfce09SScott Long 348a80d527fSLukas Ertl # There *must* be a username, and it must not exist. If 349a80d527fSLukas Ertl # this is an interactive session give the user an 350a80d527fSLukas Ertl # opportunity to retry. 3517cdfce09SScott Long # 3527cdfce09SScott Long if [ -z "$_input" ]; then 3537cdfce09SScott Long err "You must enter a username!" 3547cdfce09SScott Long [ -z "$fflag" ] && continue 3557cdfce09SScott Long fi 356b3733389SDag-Erling Smørgrav if ${PWCMD} usershow "$_input" > /dev/null 2>&1; then 357a80d527fSLukas Ertl err "User exists!" 358a80d527fSLukas Ertl [ -z "$fflag" ] && continue 359a80d527fSLukas Ertl fi 3607cdfce09SScott Long break 3617cdfce09SScott Long done 3627cdfce09SScott Long username="$_input" 3637cdfce09SScott Long} 3647cdfce09SScott Long 3657cdfce09SScott Long# get_gecos 3667cdfce09SScott Long# Reads extra information about the user. Can be used both in interactive 3677cdfce09SScott Long# and batch (from file) mode. 3687cdfce09SScott Long# 3697cdfce09SScott Longget_gecos() { 37046a619c6SDag-Erling Smørgrav local _input= 3717cdfce09SScott Long 3727cdfce09SScott Long # No need to take down additional user information for a configuration run. 3737cdfce09SScott Long [ -n "$configflag" ] && return 3747cdfce09SScott Long 3757cdfce09SScott Long if [ -z "$fflag" ]; then 3767cdfce09SScott Long echo -n "Full name: " 3777cdfce09SScott Long read _input 3787cdfce09SScott Long else 379170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f7 -d:)" 3807cdfce09SScott Long fi 3817cdfce09SScott Long ugecos="$_input" 3827cdfce09SScott Long} 3837cdfce09SScott Long 3847cdfce09SScott Long# get_shell 3857cdfce09SScott Long# Get the account's shell. Works in interactive and batch mode. It 386e7291a6bSMike Makonnen# accepts either the base name of the shell or the full path. 3877cdfce09SScott Long# If an invalid shell is entered it will simply use the default shell. 3887cdfce09SScott Long# 3897cdfce09SScott Longget_shell() { 39046a619c6SDag-Erling Smørgrav local _input= _fullpath= 3917cdfce09SScott Long ushell="$defaultshell" 3927cdfce09SScott Long 3937cdfce09SScott Long # Make sure the current value of the shell is a valid one 3940d640c0dSMike Makonnen if [ -z "$Sflag" ]; then 3958923e98bSMike Makonnen if ! shell_exists $ushell ; then 3968923e98bSMike Makonnen info "Using default shell ${defaultshell}." 3977cdfce09SScott Long ushell="$defaultshell" 3988923e98bSMike Makonnen fi 3990d640c0dSMike Makonnen fi 4007cdfce09SScott Long 4017cdfce09SScott Long if [ -z "$fflag" ]; then 402170d0882SDag-Erling Smørgrav echo -n "Shell ($shells) [${ushell##*/}]: " 4037cdfce09SScott Long read _input 4047cdfce09SScott Long else 405170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f9 -d:)" 4067cdfce09SScott Long fi 4077cdfce09SScott Long if [ -n "$_input" ]; then 4080d640c0dSMike Makonnen if [ -n "$Sflag" ]; then 4090d640c0dSMike Makonnen ushell="$_input" 4100d640c0dSMike Makonnen else 411170d0882SDag-Erling Smørgrav _fullpath=$(fullpath_from_shell "$_input") 4127cdfce09SScott Long if [ -n "$_fullpath" ]; then 4137cdfce09SScott Long ushell="$_fullpath" 4147cdfce09SScott Long else 4158923e98bSMike Makonnen err "Invalid shell ($_input) for user $username." 4168923e98bSMike Makonnen info "Using default shell ${defaultshell}." 4177cdfce09SScott Long ushell="$defaultshell" 4187cdfce09SScott Long fi 4197cdfce09SScott Long fi 4200d640c0dSMike Makonnen fi 4217cdfce09SScott Long} 4227cdfce09SScott Long 4237cdfce09SScott Long# get_homedir 4247cdfce09SScott Long# Reads the account's home directory. Used both with interactive input 4257cdfce09SScott Long# and batch input. 4267cdfce09SScott Long# 4277cdfce09SScott Longget_homedir() { 42846a619c6SDag-Erling Smørgrav local _input= 4297cdfce09SScott Long if [ -z "$fflag" ]; then 4307cdfce09SScott Long echo -n "Home directory [${homeprefix}/${username}]: " 4317cdfce09SScott Long read _input 4327cdfce09SScott Long else 433170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f8 -d:)" 4347cdfce09SScott Long fi 4357cdfce09SScott Long 4367cdfce09SScott Long if [ -n "$_input" ]; then 4377cdfce09SScott Long uhome="$_input" 4387cdfce09SScott Long # if this is a configuration run, then user input is the home 4397cdfce09SScott Long # directory prefix. Otherwise it is understood to 4407cdfce09SScott Long # be $prefix/$user 4417cdfce09SScott Long # 442170d0882SDag-Erling Smørgrav [ -z "$configflag" ] && 443170d0882SDag-Erling Smørgrav homeprefix="$(dirname "$uhome")" || 444170d0882SDag-Erling Smørgrav homeprefix="$uhome" 4457cdfce09SScott Long else 4467cdfce09SScott Long uhome="${homeprefix}/${username}" 4477cdfce09SScott Long fi 4487cdfce09SScott Long} 4497cdfce09SScott Long 45043cb08ceSMike Makonnen# get_homeperm 45143cb08ceSMike Makonnen# Reads the account's home directory permissions. 45243cb08ceSMike Makonnen# 45343cb08ceSMike Makonnenget_homeperm() { 45446a619c6SDag-Erling Smørgrav local _input= _prompt= 45543cb08ceSMike Makonnen uhomeperm=$defaultHomePerm 45643cb08ceSMike Makonnen 45743cb08ceSMike Makonnen if [ -n "$uhomeperm" ]; then 45843cb08ceSMike Makonnen _prompt="Home directory permissions [${uhomeperm}]: " 45943cb08ceSMike Makonnen else 46043cb08ceSMike Makonnen _prompt="Home directory permissions (Leave empty for default): " 46143cb08ceSMike Makonnen fi 46243cb08ceSMike Makonnen if [ -z "$fflag" ]; then 46343cb08ceSMike Makonnen echo -n "$_prompt" 46443cb08ceSMike Makonnen read _input 46543cb08ceSMike Makonnen fi 46643cb08ceSMike Makonnen 46743cb08ceSMike Makonnen if [ -n "$_input" ]; then 46843cb08ceSMike Makonnen uhomeperm="$_input" 46943cb08ceSMike Makonnen fi 47043cb08ceSMike Makonnen} 47143cb08ceSMike Makonnen 472215c0a51SJohn Grafton# get_zfs_home 473215c0a51SJohn Grafton# Determine if homeprefix is located on a ZFS filesystem and if 474215c0a51SJohn Grafton# so, enable ZFS home dataset creation. 475215c0a51SJohn Grafton# 476215c0a51SJohn Graftonget_zfs_home() { 477*0b39b2e2SMike Karels local _prefix= _tmp= 478b3733389SDag-Erling Smørgrav 479215c0a51SJohn Grafton # check if zfs kernel module is loaded before attempting to run zfs to 480215c0a51SJohn Grafton # prevent loading the kernel module on systems that don't use ZFS 4819e861827SDag-Erling Smørgrav if ! "$KLDSTATCMD" -q -m zfs; then 4829e861827SDag-Erling Smørgrav Zcreate="no" 483215c0a51SJohn Grafton return 484215c0a51SJohn Grafton fi 485b3733389SDag-Erling Smørgrav if ! _prefix=$(${ZFSCMD} list -Ho name "${homeprefix}" 2>/dev/null) || 486b3733389SDag-Erling Smørgrav [ -z "${_prefix}" ]; then 487215c0a51SJohn Grafton Zcreate="no" 488b3733389SDag-Erling Smørgrav return 489215c0a51SJohn Grafton fi 490*0b39b2e2SMike Karels # Make sure that _prefix is not a subdirectory within a dataset. If it 491*0b39b2e2SMike Karels # is, the containing dataset will be the same for it and its parent. 492*0b39b2e2SMike Karels _tmp=$(${ZFSCMD} list -Ho name "$(dirname "${homeprefix}")" 2>/dev/null) 493*0b39b2e2SMike Karels if [ "${_tmp}" = "${_prefix}" ]; then 494*0b39b2e2SMike Karels Zcreate="no" 495*0b39b2e2SMike Karels return 496*0b39b2e2SMike Karels fi 497b3733389SDag-Erling Smørgrav zhome="${_prefix}/${username}" 498215c0a51SJohn Grafton} 499215c0a51SJohn Grafton 5007cdfce09SScott Long# get_uid 5017cdfce09SScott Long# Reads a numeric userid in an interactive or batch session. Automatically 5027cdfce09SScott Long# allocates one if it is not specified. 5037cdfce09SScott Long# 5047cdfce09SScott Longget_uid() { 50546a619c6SDag-Erling Smørgrav local _input= _prompt= 5067cdfce09SScott Long uuid=${uidstart} 5077cdfce09SScott Long 5087cdfce09SScott Long if [ -n "$uuid" ]; then 509170d0882SDag-Erling Smørgrav uuid=$(get_nextuid "$uuid") 5107cdfce09SScott Long _prompt="Uid [$uuid]: " 5117cdfce09SScott Long else 5127cdfce09SScott Long _prompt="Uid (Leave empty for default): " 5137cdfce09SScott Long fi 5147cdfce09SScott Long if [ -z "$fflag" ]; then 5158b6caf26SMax Khon echo -n "$_prompt" 5167cdfce09SScott Long read _input 5177cdfce09SScott Long else 518170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f2 -d:)" 5197cdfce09SScott Long fi 5207cdfce09SScott Long 5217cdfce09SScott Long [ -n "$_input" ] && uuid=$_input 522170d0882SDag-Erling Smørgrav uuid=$(get_nextuid "$uuid") 5237cdfce09SScott Long uidstart=$uuid 5247cdfce09SScott Long} 5257cdfce09SScott Long 5267cdfce09SScott Long# get_class 5277cdfce09SScott Long# Reads login class of account. Can be used in interactive or batch mode. 5287cdfce09SScott Long# 5297cdfce09SScott Longget_class() { 53046a619c6SDag-Erling Smørgrav local _input= _class= 5317cdfce09SScott Long uclass="$defaultclass" 5327cdfce09SScott Long _class=${uclass:-"default"} 5337cdfce09SScott Long 5347cdfce09SScott Long if [ -z "$fflag" ]; then 5357cdfce09SScott Long echo -n "Login class [$_class]: " 5367cdfce09SScott Long read _input 5377cdfce09SScott Long else 538170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f4 -d:)" 5397cdfce09SScott Long fi 5407cdfce09SScott Long 5417cdfce09SScott Long [ -n "$_input" ] && uclass="$_input" 5427cdfce09SScott Long} 5437cdfce09SScott Long 5447cdfce09SScott Long# get_logingroup 5457cdfce09SScott Long# Reads user's login group. Can be used in both interactive and batch 5467cdfce09SScott Long# modes. The specified value can be a group name or its numeric id. 547642a7af5SMike Makonnen# This routine leaves the field blank if nothing is provided and 548642a7af5SMike Makonnen# a default login group has not been set. The pw(8) command 549642a7af5SMike Makonnen# will then provide a login group with the same name as the username. 5507cdfce09SScott Long# 5517cdfce09SScott Longget_logingroup() { 55246a619c6SDag-Erling Smørgrav local _input= 553642a7af5SMike Makonnen ulogingroup="$defaultLgroup" 5547cdfce09SScott Long 5557cdfce09SScott Long if [ -z "$fflag" ]; then 556642a7af5SMike Makonnen echo -n "Login group [${ulogingroup:-$username}]: " 5577cdfce09SScott Long read _input 5587cdfce09SScott Long else 559170d0882SDag-Erling Smørgrav _input="$(echo "$fileline" | cut -f3 -d:)" 5607cdfce09SScott Long fi 5617cdfce09SScott Long 5627cdfce09SScott Long # Pw(8) will use the username as login group if it's left empty 563642a7af5SMike Makonnen [ -n "$_input" ] && ulogingroup="$_input" 5647cdfce09SScott Long} 5657cdfce09SScott Long 5667cdfce09SScott Long# get_groups 5677cdfce09SScott Long# Read additional groups for the user. It can be used in both interactive 5687cdfce09SScott Long# and batch modes. 5697cdfce09SScott Long# 5707cdfce09SScott Longget_groups() { 57146a619c6SDag-Erling Smørgrav local _input= _group= 5727cdfce09SScott Long ugroups="$defaultgroups" 5737cdfce09SScott Long _group=${ulogingroup:-"${username}"} 5747cdfce09SScott Long 5757cdfce09SScott Long if [ -z "$configflag" ]; then 5767cdfce09SScott Long [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username" 5777cdfce09SScott Long [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: " 5787cdfce09SScott Long else 5797cdfce09SScott Long [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: " 5807cdfce09SScott Long fi 5817cdfce09SScott Long read _input 5827cdfce09SScott Long 5837cdfce09SScott Long [ -n "$_input" ] && ugroups="$_input" 5847cdfce09SScott Long} 5857cdfce09SScott Long 5867cdfce09SScott Long# get_expire_dates 5877cdfce09SScott Long# Read expiry information for the account and also for the password. This 5887cdfce09SScott Long# routine is used only from batch processing mode. 5897cdfce09SScott Long# 5907cdfce09SScott Longget_expire_dates() { 591170d0882SDag-Erling Smørgrav upwexpire="$(echo "$fileline" | cut -f5 -d:)" 592170d0882SDag-Erling Smørgrav uexpire="$(echo "$fileline" | cut -f6 -d:)" 5937cdfce09SScott Long} 5947cdfce09SScott Long 5957cdfce09SScott Long# get_password 5967cdfce09SScott Long# Read the password in batch processing mode. The password field matters 5977cdfce09SScott Long# only when the password type is "yes" or "random". If the field is empty and the 5987cdfce09SScott Long# password type is "yes", then it assumes the account has an empty passsword 5997cdfce09SScott Long# and changes the password type accordingly. If the password type is "random" 6007cdfce09SScott Long# and the password field is NOT empty, then it assumes the account will NOT 6017cdfce09SScott Long# have a random password and set passwdtype to "yes." 6027cdfce09SScott Long# 6037cdfce09SScott Longget_password() { 6047cdfce09SScott Long # We may temporarily change a password type. Make sure it's changed 6057cdfce09SScott Long # back to whatever it was before we process the next account. 6067cdfce09SScott Long # 607170d0882SDag-Erling Smørgrav if [ -n "$savedpwtype" ]; then 6087cdfce09SScott Long passwdtype=$savedpwtype 6097cdfce09SScott Long savedpwtype= 610170d0882SDag-Erling Smørgrav fi 6117cdfce09SScott Long 6127cdfce09SScott Long # There may be a ':' in the password 6137cdfce09SScott Long upass=${fileline#*:*:*:*:*:*:*:*:*:} 6147cdfce09SScott Long 6157cdfce09SScott Long if [ -z "$upass" ]; then 6167cdfce09SScott Long case $passwdtype in 6177cdfce09SScott Long yes) 6187cdfce09SScott Long # if it's empty, assume an empty password 6197cdfce09SScott Long passwdtype=none 6207cdfce09SScott Long savedpwtype=yes 6217cdfce09SScott Long ;; 6227cdfce09SScott Long esac 6237cdfce09SScott Long else 6247cdfce09SScott Long case $passwdtype in 6257cdfce09SScott Long random) 6267cdfce09SScott Long passwdtype=yes 6277cdfce09SScott Long savedpwtype=random 6287cdfce09SScott Long ;; 6297cdfce09SScott Long esac 6307cdfce09SScott Long fi 6317cdfce09SScott Long} 6327cdfce09SScott Long 633215c0a51SJohn Grafton# get_zfs_encryption 634215c0a51SJohn Grafton# Ask user if they want to enable encryption on their ZFS home dataset. 635215c0a51SJohn Grafton# 636215c0a51SJohn Graftonget_zfs_encryption() { 63746a619c6SDag-Erling Smørgrav local _input= _prompt= 638215c0a51SJohn Grafton _prompt="Enable ZFS encryption? (yes/no) [${Zencrypt}]: " 639215c0a51SJohn Grafton while : ; do 640215c0a51SJohn Grafton echo -n "$_prompt" 641215c0a51SJohn Grafton read _input 642215c0a51SJohn Grafton 643215c0a51SJohn Grafton [ -z "$_input" ] && _input=$Zencrypt 644215c0a51SJohn Grafton case $_input in 645215c0a51SJohn Grafton [Nn][Oo]|[Nn]) 646215c0a51SJohn Grafton Zencrypt="no" 647215c0a51SJohn Grafton break 648215c0a51SJohn Grafton ;; 649215c0a51SJohn Grafton [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 650215c0a51SJohn Grafton Zencrypt="yes" 651215c0a51SJohn Grafton break 652215c0a51SJohn Grafton ;; 653215c0a51SJohn Grafton *) 654215c0a51SJohn Grafton # invalid answer; repeat loop 655215c0a51SJohn Grafton continue 656215c0a51SJohn Grafton ;; 657215c0a51SJohn Grafton esac 658215c0a51SJohn Grafton done 659215c0a51SJohn Grafton 660215c0a51SJohn Grafton if [ "${Zencrypt}" = "yes" ]; then 661215c0a51SJohn Grafton zfsopt="-o encryption=on -o keylocation=prompt -o keyformat=passphrase" 662215c0a51SJohn Grafton fi 663215c0a51SJohn Grafton} 664215c0a51SJohn Grafton 665215c0a51SJohn Grafton# create_zfs_chrooted_dataset 666215c0a51SJohn Grafton# Create ZFS dataset owned by the user that was just added within a bsdinstall chroot 667215c0a51SJohn Grafton# 668215c0a51SJohn Graftoncreate_zfs_chrooted_dataset() { 669215c0a51SJohn Grafton if ! ${ZFSCMD} create -u ${zfsopt} "${zhome}"; then 670215c0a51SJohn Grafton err "There was an error creating ZFS dataset (${zhome})." 671215c0a51SJohn Grafton return 1 672215c0a51SJohn Grafton fi 673215c0a51SJohn Grafton ${ZFSCMD} set mountpoint=legacy "${zhome}" 674215c0a51SJohn Grafton ${MKDIRCMD} -p "${uhome}" 675215c0a51SJohn Grafton ${MOUNTCMD} -t zfs "${zhome}" "${uhome}" 676215c0a51SJohn Grafton} 677215c0a51SJohn Grafton 678215c0a51SJohn Grafton# umount_legacy_zfs 679215c0a51SJohn Grafton# Unmount ZFS home directory created as a legacy mount and switch inheritance 680215c0a51SJohn Grafton# 681215c0a51SJohn Graftonumount_legacy_zfs() { 682215c0a51SJohn Grafton ${UMOUNTCMD} "${uhome}" 683215c0a51SJohn Grafton ${ZFSCMD} inherit mountpoint "${zhome}" 684215c0a51SJohn Grafton} 685215c0a51SJohn Grafton 686215c0a51SJohn Grafton# create_zfs_dataset 687215c0a51SJohn Grafton# Create ZFS dataset owned by the user that was just added. 688215c0a51SJohn Grafton# 689215c0a51SJohn Graftoncreate_zfs_dataset() { 690215c0a51SJohn Grafton if ! ${ZFSCMD} create ${zfsopt} "${zhome}"; then 691215c0a51SJohn Grafton err "There was an error creating ZFS dataset (${zhome})." 692215c0a51SJohn Grafton return 1 693215c0a51SJohn Grafton else 694215c0a51SJohn Grafton info "Successfully created ZFS dataset (${zhome})." 695215c0a51SJohn Grafton fi 696215c0a51SJohn Grafton} 697215c0a51SJohn Grafton 698215c0a51SJohn Grafton# set_zfs_perms 699215c0a51SJohn Grafton# Give new user ownership of newly created zfs dataset. 700215c0a51SJohn Grafton# 701215c0a51SJohn Graftonset_zfs_perms() { 702215c0a51SJohn Grafton if ! ${ZFSCMD} allow "${username}" create,destroy,mount,snapshot "${zhome}"; then 703215c0a51SJohn Grafton err "There was an error setting permissions on ZFS dataset (${zhome})." 704215c0a51SJohn Grafton return 1 705215c0a51SJohn Grafton fi 706215c0a51SJohn Grafton} 707215c0a51SJohn Grafton 7087cdfce09SScott Long# input_from_file 7097cdfce09SScott Long# Reads a line of account information from standard input and 7107cdfce09SScott Long# adds it to the user database. 7117cdfce09SScott Long# 7127cdfce09SScott Longinput_from_file() { 71346a619c6SDag-Erling Smørgrav local _field= 7147cdfce09SScott Long 715c95c14d4SMike Makonnen while read -r fileline ; do 7167cdfce09SScott Long case "$fileline" in 7177cdfce09SScott Long \#*|'') 7187cdfce09SScott Long ;; 719f30cdb6eSMike Makonnen *) 7207cdfce09SScott Long get_user || continue 7217cdfce09SScott Long get_gecos 7227cdfce09SScott Long get_uid 7237cdfce09SScott Long get_logingroup 7247cdfce09SScott Long get_class 7257cdfce09SScott Long get_shell 7267cdfce09SScott Long get_homedir 727215c0a51SJohn Grafton get_zfs_home 72843cb08ceSMike Makonnen get_homeperm 7297cdfce09SScott Long get_password 7307cdfce09SScott Long get_expire_dates 73188b63febSMike Makonnen ugroups="$defaultgroups" 7327cdfce09SScott Long 7337cdfce09SScott Long add_user 734f30cdb6eSMike Makonnen ;; 735f30cdb6eSMike Makonnen esac 7367cdfce09SScott Long done 7377cdfce09SScott Long} 7387cdfce09SScott Long 7397cdfce09SScott Long# input_interactive 7407cdfce09SScott Long# Prompts for user information interactively, and commits to 7417cdfce09SScott Long# the user database. 7427cdfce09SScott Long# 7437cdfce09SScott Longinput_interactive() { 74446a619c6SDag-Erling Smørgrav local _disable= _pass= _passconfirm= _input= 745170d0882SDag-Erling Smørgrav local _random="no" 746170d0882SDag-Erling Smørgrav local _emptypass="no" 747170d0882SDag-Erling Smørgrav local _usepass="yes" 748170d0882SDag-Erling Smørgrav local _logingroup_ok="no" 749170d0882SDag-Erling Smørgrav local _groups_ok="no" 750170d0882SDag-Erling Smørgrav local _all_ok="yes" 7517cdfce09SScott Long case $passwdtype in 7527cdfce09SScott Long none) 7537cdfce09SScott Long _emptypass="yes" 7547cdfce09SScott Long _usepass="yes" 7557cdfce09SScott Long ;; 7567cdfce09SScott Long no) 7577cdfce09SScott Long _usepass="no" 7587cdfce09SScott Long ;; 7597cdfce09SScott Long random) 7607cdfce09SScott Long _random="yes" 7617cdfce09SScott Long ;; 7627cdfce09SScott Long esac 7637cdfce09SScott Long 7647cdfce09SScott Long get_user 7657cdfce09SScott Long get_gecos 7667cdfce09SScott Long get_uid 7673386ded6SAdrian Chadd 7683386ded6SAdrian Chadd # The case where group = user is handled elsewhere, so 7693386ded6SAdrian Chadd # validate any other groups the user is invited to. 7703386ded6SAdrian Chadd until [ "$_logingroup_ok" = yes ]; do 7717cdfce09SScott Long get_logingroup 7723386ded6SAdrian Chadd _logingroup_ok=yes 773170d0882SDag-Erling Smørgrav if [ -n "$ulogingroup" ] && [ "$username" != "$ulogingroup" ]; then 7743386ded6SAdrian Chadd if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then 7753386ded6SAdrian Chadd echo "Group $ulogingroup does not exist!" 7763386ded6SAdrian Chadd _logingroup_ok=no 7773386ded6SAdrian Chadd fi 7783386ded6SAdrian Chadd fi 7793386ded6SAdrian Chadd done 7803386ded6SAdrian Chadd until [ "$_groups_ok" = yes ]; do 7817cdfce09SScott Long get_groups 7823386ded6SAdrian Chadd _groups_ok=yes 7833386ded6SAdrian Chadd for i in $ugroups; do 7843386ded6SAdrian Chadd if [ "$username" != "$i" ]; then 7853386ded6SAdrian Chadd if ! ${PWCMD} show group $i > /dev/null 2>&1; then 7863386ded6SAdrian Chadd echo "Group $i does not exist!" 7873386ded6SAdrian Chadd _groups_ok=no 7883386ded6SAdrian Chadd fi 7893386ded6SAdrian Chadd fi 7903386ded6SAdrian Chadd done 7913386ded6SAdrian Chadd done 7923386ded6SAdrian Chadd 7937cdfce09SScott Long get_class 7947cdfce09SScott Long get_shell 7957cdfce09SScott Long get_homedir 79643cb08ceSMike Makonnen get_homeperm 797215c0a51SJohn Grafton get_zfs_home 798215c0a51SJohn Grafton [ "$Zcreate" = "yes" ] && get_zfs_encryption 7997cdfce09SScott Long 8007cdfce09SScott Long while : ; do 8017cdfce09SScott Long echo -n "Use password-based authentication? [$_usepass]: " 8027cdfce09SScott Long read _input 8037cdfce09SScott Long [ -z "$_input" ] && _input=$_usepass 8047cdfce09SScott Long case $_input in 8057cdfce09SScott Long [Nn][Oo]|[Nn]) 8067cdfce09SScott Long passwdtype="no" 8077cdfce09SScott Long ;; 8087cdfce09SScott Long [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 8097cdfce09SScott Long while : ; do 8107cdfce09SScott Long echo -n "Use an empty password? (yes/no) [$_emptypass]: " 8117cdfce09SScott Long read _input 8127cdfce09SScott Long [ -n "$_input" ] && _emptypass=$_input 8137cdfce09SScott Long case $_emptypass in 8147cdfce09SScott Long [Nn][Oo]|[Nn]) 8157cdfce09SScott Long echo -n "Use a random password? (yes/no) [$_random]: " 8167cdfce09SScott Long read _input 8177cdfce09SScott Long [ -n "$_input" ] && _random="$_input" 8187cdfce09SScott Long case $_random in 8197cdfce09SScott Long [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 8207cdfce09SScott Long passwdtype="random" 8217cdfce09SScott Long break 8227cdfce09SScott Long ;; 8237cdfce09SScott Long esac 8247cdfce09SScott Long passwdtype="yes" 825187a97aaSMike Makonnen [ -n "$configflag" ] && break 8267cdfce09SScott Long trap 'stty echo; exit' 0 1 2 3 15 8277cdfce09SScott Long stty -echo 8287cdfce09SScott Long echo -n "Enter password: " 829f5339b09SKyle Evans IFS= read -r upass 8307cdfce09SScott Long echo'' 8317cdfce09SScott Long echo -n "Enter password again: " 832f5339b09SKyle Evans IFS= read -r _passconfirm 8337cdfce09SScott Long echo '' 8347cdfce09SScott Long stty echo 8357cdfce09SScott Long # if user entered a blank password 8367cdfce09SScott Long # explicitly ask again. 837170d0882SDag-Erling Smørgrav [ -z "$upass$_passconfirm" ] && continue 8387cdfce09SScott Long ;; 8397cdfce09SScott Long [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 8407cdfce09SScott Long passwdtype="none" 8417cdfce09SScott Long break; 8427cdfce09SScott Long ;; 8437cdfce09SScott Long *) 8447cdfce09SScott Long # invalid answer; repeat the loop 8457cdfce09SScott Long continue 8467cdfce09SScott Long ;; 8477cdfce09SScott Long esac 8487cdfce09SScott Long if [ "$upass" != "$_passconfirm" ]; then 8497cdfce09SScott Long echo "Passwords did not match!" 8507cdfce09SScott Long continue 8517cdfce09SScott Long fi 8527cdfce09SScott Long break 8537cdfce09SScott Long done 8547cdfce09SScott Long ;; 8557cdfce09SScott Long *) 8567cdfce09SScott Long # invalid answer; repeat loop 8577cdfce09SScott Long continue 8587cdfce09SScott Long ;; 8597cdfce09SScott Long esac 8607cdfce09SScott Long break; 8617cdfce09SScott Long done 8627cdfce09SScott Long _disable=${disableflag:-"no"} 8637cdfce09SScott Long while : ; do 8647cdfce09SScott Long echo -n "Lock out the account after creation? [$_disable]: " 8657cdfce09SScott Long read _input 8667cdfce09SScott Long [ -z "$_input" ] && _input=$_disable 8677cdfce09SScott Long case $_input in 8687cdfce09SScott Long [Nn][Oo]|[Nn]) 8697cdfce09SScott Long disableflag= 8707cdfce09SScott Long ;; 8717cdfce09SScott Long [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 8727cdfce09SScott Long disableflag=yes 8737cdfce09SScott Long ;; 8747cdfce09SScott Long *) 8757cdfce09SScott Long # invalid answer; repeat loop 8767cdfce09SScott Long continue 8777cdfce09SScott Long ;; 8787cdfce09SScott Long esac 8797cdfce09SScott Long break 8807cdfce09SScott Long done 8817cdfce09SScott Long 8827cdfce09SScott Long # Display the information we have so far and prompt to 8837cdfce09SScott Long # commit it. 8847cdfce09SScott Long # 8857cdfce09SScott Long _disable=${disableflag:-"no"} 886215c0a51SJohn Grafton [ -z "$configflag" ] && printf "%-11s : %s\n" Username $username 8877cdfce09SScott Long case $passwdtype in 8887cdfce09SScott Long yes) 8897cdfce09SScott Long _pass='*****' 8907cdfce09SScott Long ;; 8917cdfce09SScott Long no) 8927cdfce09SScott Long _pass='<disabled>' 8937cdfce09SScott Long ;; 8947cdfce09SScott Long none) 8957cdfce09SScott Long _pass='<blank>' 8967cdfce09SScott Long ;; 8977cdfce09SScott Long random) 8987cdfce09SScott Long _pass='<random>' 8997cdfce09SScott Long ;; 9007cdfce09SScott Long esac 901215c0a51SJohn Grafton [ -z "$configflag" ] && printf "%-11s : %s\n" "Password" "$_pass" 902215c0a51SJohn Grafton [ -n "$configflag" ] && printf "%-11s : %s\n" "Pass Type" "$passwdtype" 903215c0a51SJohn Grafton [ -z "$configflag" ] && printf "%-11s : %s\n" "Full Name" "$ugecos" 904215c0a51SJohn Grafton [ -z "$configflag" ] && printf "%-11s : %s\n" "Uid" "$uuid" 905170d0882SDag-Erling Smørgrav [ "$Zcreate" = "yes" ] && [ -z "$configflag" ] && 906170d0882SDag-Erling Smørgrav printf "%-11s : %s\n" "ZFS dataset" "${zhome}" 907170d0882SDag-Erling Smørgrav [ "$Zencrypt" = "yes" ] && [ -z "$configflag" ] && 908170d0882SDag-Erling Smørgrav printf "%-11s : %s\n" "Encrypted" "${Zencrypt}" 909215c0a51SJohn Grafton printf "%-11s : %s\n" "Class" "$uclass" 910215c0a51SJohn Grafton printf "%-11s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups" 911215c0a51SJohn Grafton printf "%-11s : %s\n" "Home" "$uhome" 912215c0a51SJohn Grafton printf "%-11s : %s\n" "Home Mode" "$uhomeperm" 913215c0a51SJohn Grafton printf "%-11s : %s\n" "Shell" "$ushell" 914215c0a51SJohn Grafton printf "%-11s : %s\n" "Locked" "$_disable" 9157cdfce09SScott Long while : ; do 9169efad6f9SSven Ruediger echo -n "OK? (yes/no) [$_all_ok]: " 9177cdfce09SScott Long read _input 9189efad6f9SSven Ruediger if [ -z "$_input" ]; then 9199efad6f9SSven Ruediger _input=$_all_ok 9209efad6f9SSven Ruediger fi 9217cdfce09SScott Long case $_input in 9227cdfce09SScott Long [Nn][Oo]|[Nn]) 9237cdfce09SScott Long return 1 9247cdfce09SScott Long ;; 9257cdfce09SScott Long [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 9267cdfce09SScott Long add_user 9277cdfce09SScott Long ;; 9287cdfce09SScott Long *) 9297cdfce09SScott Long continue 9307cdfce09SScott Long ;; 9317cdfce09SScott Long esac 9327cdfce09SScott Long break 9337cdfce09SScott Long done 9347cdfce09SScott Long return 0 9357cdfce09SScott Long} 9367cdfce09SScott Long 937989090c0SRobert Drehmel#### END SUBROUTINE DEFINITION #### 9387cdfce09SScott Long 939170d0882SDag-Erling SmørgravTHISCMD=${0##*/} 9407cdfce09SScott LongDEFAULTSHELL=/bin/sh 9417cdfce09SScott LongADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}" 9427cdfce09SScott LongPWCMD="${PWCMD:-/usr/sbin/pw}" 9437cdfce09SScott LongMAILCMD="${MAILCMD:-mail}" 9447cdfce09SScott LongETCSHELLS="${ETCSHELLS:-/etc/shells}" 9450d640c0dSMike MakonnenNOHOME="/nonexistent" 9468923e98bSMike MakonnenNOLOGIN="nologin" 94768050033SColin PercivalNOLOGIN_PATH="/usr/sbin/nologin" 948e63bd70aSMax KhonGREPCMD="/usr/bin/grep" 949e63bd70aSMax KhonDATECMD="/bin/date" 950215c0a51SJohn GraftonMKDIRCMD="/bin/mkdir" 951215c0a51SJohn GraftonMOUNTCMD="/sbin/mount" 952215c0a51SJohn GraftonUMOUNTCMD="/sbin/umount" 953215c0a51SJohn GraftonZFSCMD="/sbin/zfs" 954215c0a51SJohn GraftonKLDSTATCMD="/sbin/kldstat" 9557cdfce09SScott Long 9567cdfce09SScott Long# Set default values 9577cdfce09SScott Long# 9587cdfce09SScott Longusername= 9597cdfce09SScott Longuuid= 9607cdfce09SScott Longuidstart= 9617cdfce09SScott Longugecos= 9627cdfce09SScott Longulogingroup= 9637cdfce09SScott Longuclass= 9647cdfce09SScott Longuhome= 96543cb08ceSMike Makonnenuhomeperm= 9667cdfce09SScott Longupass= 9677cdfce09SScott Longushell= 9687cdfce09SScott Longudotdir=/usr/share/skel 9697cdfce09SScott Longugroups= 9707cdfce09SScott Longuexpire= 9717cdfce09SScott Longupwexpire= 972170d0882SDag-Erling Smørgravshells="$(valid_shells)" 9737cdfce09SScott Longpasswdtype="yes" 9747cdfce09SScott Longmsgfile=/etc/adduser.msg 9757cdfce09SScott Longmsgflag= 9767cdfce09SScott Longquietflag= 9777cdfce09SScott Longconfigflag= 9787cdfce09SScott Longfflag= 9797cdfce09SScott Longinfile= 9807cdfce09SScott Longdisableflag= 9810d640c0dSMike MakonnenDflag= 9820d640c0dSMike MakonnenSflag= 983215c0a51SJohn GraftonZcreate="yes" 9847cdfce09SScott Longreadconfig="yes" 9857cdfce09SScott Longhomeprefix="/home" 9867cdfce09SScott Longrandompass= 9877cdfce09SScott Longfileline= 9887cdfce09SScott Longsavedpwtype= 9897cdfce09SScott Longdefaultclass= 990642a7af5SMike MakonnendefaultLgroup= 991fdbc43bdSMike Makonnendefaultgroups= 9927cdfce09SScott Longdefaultshell="${DEFAULTSHELL}" 99343cb08ceSMike MakonnendefaultHomePerm= 994215c0a51SJohn Graftonzfsopt= 995215c0a51SJohn GraftonZencrypt="no" 9967cdfce09SScott Long 9977cdfce09SScott Long# Make sure the user running this program is root. This isn't a security 99805c9bdf5SJoel Dahl# measure as much as it is a useful method of reminding the user to 9997cdfce09SScott Long# 'su -' before he/she wastes time entering data that won't be saved. 10007cdfce09SScott Long# 1001170d0882SDag-Erling Smørgravprocowner=${procowner:-$(/usr/bin/id -u)} 10027cdfce09SScott Longif [ "$procowner" != "0" ]; then 10037cdfce09SScott Long err 'you must be the super-user (uid 0) to use this utility.' 10047cdfce09SScott Long exit 1 10057cdfce09SScott Longfi 10067cdfce09SScott Long 10073df5ecacSUlrich Spörlein# Override from our conf file 10087cdfce09SScott Long# Quickly go through the commandline line to see if we should read 10097cdfce09SScott Long# from our configuration file. The actual parsing of the commandline 10107cdfce09SScott Long# arguments happens after we read in our configuration file (commandline 10117cdfce09SScott Long# should override configuration file). 10127cdfce09SScott Long# 10137cdfce09SScott Longfor _i in $* ; do 10147cdfce09SScott Long if [ "$_i" = "-N" ]; then 10157cdfce09SScott Long readconfig= 10167cdfce09SScott Long break; 10177cdfce09SScott Long fi 10187cdfce09SScott Longdone 1019170d0882SDag-Erling Smørgravif [ -n "$readconfig" ] && [ -r "${ADDUSERCONF}" ]; then 1020170d0882SDag-Erling Smørgrav . "${ADDUSERCONF}" 10217cdfce09SScott Longfi 10227cdfce09SScott Long 10233df5ecacSUlrich Spörlein# Process command-line options 10247cdfce09SScott Long# 10257cdfce09SScott Longfor _switch ; do 10267cdfce09SScott Long case $_switch in 10277cdfce09SScott Long -L) 10287cdfce09SScott Long defaultclass="$2" 10297cdfce09SScott Long shift; shift 10307cdfce09SScott Long ;; 10317cdfce09SScott Long -C) 10327cdfce09SScott Long configflag=yes 10337cdfce09SScott Long shift 10347cdfce09SScott Long ;; 10350d640c0dSMike Makonnen -D) 10360d640c0dSMike Makonnen Dflag=yes 10370d640c0dSMike Makonnen shift 10380d640c0dSMike Makonnen ;; 10397cdfce09SScott Long -E) 10407cdfce09SScott Long disableflag=yes 10417cdfce09SScott Long shift 10427cdfce09SScott Long ;; 10437cdfce09SScott Long -k) 10447cdfce09SScott Long udotdir="$2" 10457cdfce09SScott Long shift; shift 10467cdfce09SScott Long ;; 10477cdfce09SScott Long -f) 10487cdfce09SScott Long [ "$2" != "-" ] && infile="$2" 10497cdfce09SScott Long fflag=yes 10507cdfce09SScott Long shift; shift 10517cdfce09SScott Long ;; 1052642a7af5SMike Makonnen -g) 1053642a7af5SMike Makonnen defaultLgroup="$2" 1054642a7af5SMike Makonnen shift; shift 1055642a7af5SMike Makonnen ;; 10567cdfce09SScott Long -G) 10577cdfce09SScott Long defaultgroups="$2" 10587cdfce09SScott Long shift; shift 10597cdfce09SScott Long ;; 10607cdfce09SScott Long -h) 10617cdfce09SScott Long show_usage 10627cdfce09SScott Long exit 0 10637cdfce09SScott Long ;; 10647cdfce09SScott Long -d) 10657cdfce09SScott Long homeprefix="$2" 10667cdfce09SScott Long shift; shift 10677cdfce09SScott Long ;; 10687cdfce09SScott Long -m) 10697cdfce09SScott Long case "$2" in 10707cdfce09SScott Long [Nn][Oo]) 10717cdfce09SScott Long msgflag= 10727cdfce09SScott Long ;; 10737cdfce09SScott Long *) 10747cdfce09SScott Long msgflag=yes 10757cdfce09SScott Long msgfile="$2" 10767cdfce09SScott Long ;; 10777cdfce09SScott Long esac 10787cdfce09SScott Long shift; shift 10797cdfce09SScott Long ;; 108043cb08ceSMike Makonnen -M) 108143cb08ceSMike Makonnen defaultHomePerm=$2 108243cb08ceSMike Makonnen shift; shift 108343cb08ceSMike Makonnen ;; 10847cdfce09SScott Long -N) 10857cdfce09SScott Long readconfig= 10867cdfce09SScott Long shift 10877cdfce09SScott Long ;; 10887cdfce09SScott Long -w) 10897cdfce09SScott Long case "$2" in 10907cdfce09SScott Long no|none|random|yes) 10917cdfce09SScott Long passwdtype=$2 10927cdfce09SScott Long ;; 10937cdfce09SScott Long *) 10947cdfce09SScott Long show_usage 10957cdfce09SScott Long exit 1 10967cdfce09SScott Long ;; 10977cdfce09SScott Long esac 10987cdfce09SScott Long shift; shift 10997cdfce09SScott Long ;; 11007cdfce09SScott Long -q) 11017cdfce09SScott Long quietflag=yes 11027cdfce09SScott Long shift 11037cdfce09SScott Long ;; 11047cdfce09SScott Long -s) 1105170d0882SDag-Erling Smørgrav defaultshell="$(fullpath_from_shell $2)" 11067cdfce09SScott Long shift; shift 11077cdfce09SScott Long ;; 11080d640c0dSMike Makonnen -S) 11090d640c0dSMike Makonnen Sflag=yes 11100d640c0dSMike Makonnen shift 11110d640c0dSMike Makonnen ;; 11127cdfce09SScott Long -u) 11137cdfce09SScott Long uidstart=$2 11147cdfce09SScott Long shift; shift 11157cdfce09SScott Long ;; 1116215c0a51SJohn Grafton -Z) 1117215c0a51SJohn Grafton Zcreate="no" 1118215c0a51SJohn Grafton shift 1119215c0a51SJohn Grafton ;; 11207cdfce09SScott Long esac 11217cdfce09SScott Longdone 11227cdfce09SScott Long 11237cdfce09SScott Long# If the -f switch was used, get input from a file. Otherwise, 11247cdfce09SScott Long# this is an interactive session. 11257cdfce09SScott Long# 11267cdfce09SScott Longif [ -n "$fflag" ]; then 11277cdfce09SScott Long if [ -z "$infile" ]; then 11287cdfce09SScott Long input_from_file 11297cdfce09SScott Long elif [ -n "$infile" ]; then 11307cdfce09SScott Long if [ -r "$infile" ]; then 11317cdfce09SScott Long input_from_file < $infile 11327cdfce09SScott Long else 11337cdfce09SScott Long err "File ($infile) is unreadable or does not exist." 11347cdfce09SScott Long fi 11357cdfce09SScott Long fi 11367cdfce09SScott Longelse 11377cdfce09SScott Long input_interactive 113821084fe3SMike Makonnen while : ; do 113946c4e86eSDag-Erling Smørgrav _another_user="no" 1140187a97aaSMike Makonnen if [ -z "$configflag" ]; then 11419efad6f9SSven Ruediger echo -n "Add another user? (yes/no) [$_another_user]: " 1142187a97aaSMike Makonnen else 11439efad6f9SSven Ruediger echo -n "Re-edit the default configuration? (yes/no) [$_another_user]: " 1144187a97aaSMike Makonnen fi 114521084fe3SMike Makonnen read _input 11469efad6f9SSven Ruediger if [ -z "$_input" ]; then 11479efad6f9SSven Ruediger _input=$_another_user 11489efad6f9SSven Ruediger fi 114921084fe3SMike Makonnen case $_input in 115021084fe3SMike Makonnen [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 1151170d0882SDag-Erling Smørgrav uidstart=$(get_nextuid $uidstart) 115221084fe3SMike Makonnen input_interactive 115321084fe3SMike Makonnen continue 115421084fe3SMike Makonnen ;; 115521084fe3SMike Makonnen [Nn][Oo]|[Nn]) 115621084fe3SMike Makonnen echo "Goodbye!" 115721084fe3SMike Makonnen ;; 115821084fe3SMike Makonnen *) 115921084fe3SMike Makonnen continue 116021084fe3SMike Makonnen ;; 116121084fe3SMike Makonnen esac 116221084fe3SMike Makonnen break 116321084fe3SMike Makonnen done 11647cdfce09SScott Longfi 1165