diff options
Diffstat (limited to 'usr.sbin/adduser')
-rw-r--r-- | usr.sbin/adduser/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/adduser/Makefile.depend | 11 | ||||
-rw-r--r-- | usr.sbin/adduser/adduser.8 | 479 | ||||
-rw-r--r-- | usr.sbin/adduser/adduser.conf.5 | 221 | ||||
-rw-r--r-- | usr.sbin/adduser/adduser.sh | 1051 | ||||
-rw-r--r-- | usr.sbin/adduser/rmuser.8 | 210 | ||||
-rw-r--r-- | usr.sbin/adduser/rmuser.sh | 361 |
7 files changed, 2339 insertions, 0 deletions
diff --git a/usr.sbin/adduser/Makefile b/usr.sbin/adduser/Makefile new file mode 100644 index 0000000..0ca2dae --- /dev/null +++ b/usr.sbin/adduser/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SCRIPTS=adduser.sh rmuser.sh +MAN= adduser.conf.5 adduser.8 rmuser.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/adduser/Makefile.depend b/usr.sbin/adduser/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/usr.sbin/adduser/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8 new file mode 100644 index 0000000..2e6a5b5 --- /dev/null +++ b/usr.sbin/adduser/adduser.8 @@ -0,0 +1,479 @@ +.\" Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +.\" All rights reserved. +.\" Copyright (c) 2002-2004 Michael Telahun Makonnen <mtm@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 15, 2012 +.Dt ADDUSER 8 +.Os +.Sh NAME +.Nm adduser +.Nd command for adding new users +.Sh SYNOPSIS +.Nm +.Op Fl CDENShq +.Op Fl G Ar groups +.Op Fl L Ar login_class +.Op Fl M Ar mode +.Op Fl d Ar partition +.Op Fl f Ar file +.Op Fl g Ar login_group +.Op Fl k Ar dotdir +.Op Fl m Ar message_file +.Op Fl s Ar shell +.Op Fl u Ar uid_start +.Op Fl w Ar type +.Sh DESCRIPTION +The +.Nm +utility is a shell script, implemented around the +.Xr pw 8 +command, for adding new users. +It creates passwd/group entries, a home directory, +copies dotfiles and sends the new user a welcome message. +It supports two modes of operation. +It may be used interactively +at the command line to add one user at a time, or it may be directed +to get the list of new users from a file and operate in batch mode +without requiring any user interaction. +.Sh RESTRICTIONS +.Bl -tag -width indent +.It username +Login name. +The user name is restricted to whatever +.Xr pw 8 +will accept. +Generally this means it +may contain only lowercase characters or digits but cannot begin with the +.Ql - +character. +Maximum length +is 16 characters. +The reasons for this limit are historical. +Given that people have traditionally wanted to break this +limit for aesthetic reasons, it has never been of great importance to break +such a basic fundamental parameter in +.Ux . +You can change +.Dv UT_NAMESIZE +in +.In utmp.h +and recompile the +world; people have done this and it works, but you will have problems +with any precompiled programs, or source that assumes the 8-character +name limit, such as NIS. +The NIS protocol mandates an 8-character username. +If you need a longer login name for e-mail addresses, +you can define an alias in +.Pa /etc/mail/aliases . +.It "full name" +This is typically known as the gecos field and usually contains +the user's full name. +Additionally, it may contain a comma separated +list of values such as office number and work and home phones. +If the +name contains an ampersand it will be replaced by the capitalized +login name when displayed by other programs. +The +.Ql \&: +character is not allowed. +.It shell +Unless the +.Fl S +argument is supplied only valid shells from the shell database +.Pq Pa /etc/shells +are allowed. +In addition, +either the base name or the full path of the shell may be supplied. +.It UID +Automatically generated or your choice. +It must be less than 32000. +.It "GID/login group" +Automatically generated or your choice. +It must be less than 32000. +.It password +You may choose an empty password, disable the password, use a +randomly generated password or specify your own plaintext password, +which will be encrypted before being stored in the user database. +.El +.Sh UNIQUE GROUPS +Perhaps you are missing what +.Em can +be done with this scheme that falls apart +with most other schemes. +With each user in their own group, +they can safely run with a umask of 002 instead of the usual 022 +and create files in their home directory +without worrying about others being able to change them. +.Pp +For a shared area you create a separate UID/GID, you place each person +that should be able to access this area into that new group. +.Pp +This model of UID/GID administration allows far greater flexibility than lumping +users into groups and having to muck with the umask when working in a shared +area. +.Pp +I have been using this model for almost 10 years and found that it works +for most situations, and has never gotten in the way. +(Rod Grimes) +.Sh CONFIGURATION +The +.Nm +utility reads its configuration information from +.Pa /etc/adduser.conf . +If this file does not exist, it will use predefined defaults. +While this file may be edited by hand, +the safer option is to use the +.Fl C +command line argument. +With this argument, +.Nm +will start interactive input, save the answers to its prompts in +.Pa /etc/adduser.conf , +and promptly exit without modifying the user +database. +Options specified on the command line will take precedence over +any values saved in this file. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl C +Create new configuration file and exit. +This option is mutually exclusive with the +.Fl f +option. +.It Fl d Ar partition +Home partition. +Default partition, under which all user directories +will be located. +The +.Pa /nonexistent +partition is considered special. +The +.Nm +script will not create and populate a home directory by that name. +Otherwise, +by default it attempts to create a home directory. +.It Fl D +Do not attempt to create the home directory. +.It Fl E +Disable the account. +This option will lock the account by prepending the string +.Dq Li *LOCKED* +to the password field. +The account may be unlocked +by the super-user with the +.Xr pw 8 +command: +.Pp +.D1 Nm pw Cm unlock Op Ar name | uid +.It Fl f Ar file +Get the list of accounts to create from +.Ar file . +If +.Ar file +is +.Dq Fl , +then get the list from standard input. +If this option is specified, +.Nm +will operate in batch mode and will not seek any user input. +If an error is encountered while processing an account, it will write a +message to standard error and move to the next account. +The format +of the input file is described below. +.It Fl g Ar login_group +Normally, +if no login group is specified, +it is assumed to be the same as the username. +This option makes +.Ar login_group +the default. +.It Fl G Ar groups +Space-separated list of additional groups. +This option allows the user to specify additional groups to add users to. +The user is a member of these groups in addition to their login group. +.It Fl h +Print a summary of options and exit. +.It Fl k Ar directory +Copy files from +.Ar directory +into the home +directory of new users; +.Pa dot.foo +will be renamed to +.Pa .foo . +.It Fl L Ar login_class +Set default login class. +.It Fl m Ar file +Send new users a welcome message from +.Ar file . +Specifying a value of +.Cm no +for +.Ar file +causes no message to be sent to new users. +Please note that the message +file can reference the internal variables of the +.Nm +script. +.It Fl M Ar mode +Create the home directory with permissions set to +.Ar mode . +.It Fl N +Do not read the default configuration file. +.It Fl q +Minimal user feedback. +In particular, the random password will not be echoed to +standard output. +.It Fl s Ar shell +Default shell for new users. +The +.Ar shell +argument may be the base name of the shell or the full path. +Unless the +.Fl S +argument is supplied the shell must exist in +.Pa /etc/shells +or be the special shell +.Em nologin +to be considered a valid shell. +.It Fl S +The existence or validity of the specified shell will not be checked. +.It Fl u Ar uid +Use UIDs from +.Ar uid +on up. +.It Fl w Ar type +Password type. +The +.Nm +utility allows the user to specify what type of password to create. +The +.Ar type +argument may have one of the following values: +.Bl -tag -width ".Cm random" +.It Cm no +Disable the password. +Instead of an encrypted string, the password field will contain a single +.Ql * +character. +The user may not log in until the super-user +manually enables the password. +.It Cm none +Use an empty string as the password. +.It Cm yes +Use a user-supplied string as the password. +In interactive mode, +the user will be prompted for the password. +In batch mode, the +last (10th) field in the line is assumed to be the password. +.It Cm random +Generate a random string and use it as a password. +The password will be echoed to standard output. +In addition, it will be available for inclusion in the message file in the +.Va randompass +variable. +.El +.El +.Sh FORMAT +When the +.Fl f +option is used, the account information must be stored in a specific +format. +All empty lines or lines beginning with a +.Ql # +will be ignored. +All other lines must contain ten colon +.Pq Ql \&: +separated fields as described below. +Command line options do not take precedence +over values in the fields. +Only the password field may contain a +.Ql \&: +character as part of the string. +.Pp +.Sm off +.D1 Ar name : uid : gid : class : change : expire : gecos : home_dir : shell : password +.Sm on +.Bl -tag -width ".Ar password" +.It Ar name +Login name. +This field may not be empty. +.It Ar uid +Numeric login user ID. +If this field is left empty, it will be automatically generated. +.It Ar gid +Numeric primary group ID. +If this field is left empty, a group with the +same name as the user name will be created and its GID will be used +instead. +.It Ar class +Login class. +This field may be left empty. +.It Ar change +Password ageing. +This field denotes the password change date for the account. +The format of this field is the same as the format of the +.Fl p +argument to +.Xr pw 8 . +It may be +.Ar dd Ns - Ns Ar mmm Ns - Ns Ar yy Ns Op Ar yy , +where +.Ar dd +is for the day, +.Ar mmm +is for the month in numeric or alphabetical format: +.Dq Li 10 +or +.Dq Li Oct , +and +.Ar yy Ns Op Ar yy +is the four or two digit year. +To denote a time relative to the current date the format is: +.No + Ns Ar n Ns Op Ar mhdwoy , +where +.Ar n +denotes a number, followed by the minutes, hours, days, weeks, +months or years after which the password must be changed. +This field may be left empty to turn it off. +.It Ar expire +Account expiration. +This field denotes the expiry date of the account. +The account may not be used after the specified date. +The format of this field is the same as that for password ageing. +This field may be left empty to turn it off. +.It Ar gecos +Full name and other extra information about the user. +.It Ar home_dir +Home directory. +If this field is left empty, it will be automatically +created by appending the username to the home partition. +The +.Pa /nonexistent +home directory is considered special and +is understood to mean that no home directory is to be +created for the user. +.It Ar shell +Login shell. +This field should contain either the base name or +the full path to a valid login shell. +.It Ar password +User password. +This field should contain a plaintext string, which will +be encrypted before being placed in the user database. +If the password type is +.Cm yes +and this field is empty, it is assumed the account will have an empty password. +If the password type is +.Cm random +and this field is +.Em not +empty, its contents will be used +as a password. +This field will be ignored if the +.Fl w +option is used with a +.Cm no +or +.Cm none +argument. +Be careful not to terminate this field with a closing +.Ql \&: +because it will be treated as part of the password. +.El +.Sh FILES +.Bl -tag -width ".Pa /etc/adduser.message" -compact +.It Pa /etc/master.passwd +user database +.It Pa /etc/group +group database +.It Pa /etc/shells +shell database +.It Pa /etc/login.conf +login classes database +.It Pa /etc/adduser.conf +configuration file for +.Nm +.It Pa /etc/adduser.message +message file for +.Nm +.It Pa /usr/share/skel +skeletal login directory +.It Pa /var/log/adduser +logfile for +.Nm +.El +.Sh SEE ALSO +.Xr chpass 1 , +.Xr passwd 1 , +.Xr adduser.conf 5 , +.Xr aliases 5 , +.Xr group 5 , +.Xr login.conf 5 , +.Xr passwd 5 , +.Xr shells 5 , +.Xr adding_user 8 , +.Xr pw 8 , +.Xr pwd_mkdb 8 , +.Xr rmuser 8 , +.Xr vipw 8 , +.Xr yp 8 +.Sh HISTORY +The +.Nm +command appeared in +.Fx 2.1 . +.Sh AUTHORS +.An -nosplit +This manual page and the original script, in Perl, was written by +.An Wolfram Schneider Aq Mt wosch@FreeBSD.org . +The replacement script, written as a Bourne +shell script with some enhancements, and the man page modification that +came with it were done by +.An Mike Makonnen Aq Mt mtm@identd.net . +.Sh BUGS +In order for +.Nm +to correctly expand variables such as +.Va $username +and +.Va $randompass +in the message sent to new users, it must let the shell evaluate +each line of the message file. +This means that shell commands can also be embedded in the message file. +The +.Nm +utility attempts to mitigate the possibility of an attacker using this +feature by refusing to evaluate the file if it is not owned and writable +only by the root user. +In addition, shell special characters and operators will have to be +escaped when used in the message file. +.Pp +Also, password ageing and account expiry times are currently settable +only in batch mode or when specified in +.Pa /etc/adduser.conf . +The user should be able to set them in interactive mode as well. diff --git a/usr.sbin/adduser/adduser.conf.5 b/usr.sbin/adduser/adduser.conf.5 new file mode 100644 index 0000000..af9fe22 --- /dev/null +++ b/usr.sbin/adduser/adduser.conf.5 @@ -0,0 +1,221 @@ +.\" +.\" Copyright (c) 2004 Tom Rhodes +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd April 12, 2007 +.Dt ADDUSER.CONF 5 +.Os +.Sh NAME +.Nm adduser.conf +.Nd +.Xr adduser 8 +configuration file +.Sh DESCRIPTION +The +.Pa /etc/adduser.conf +file is automatically generated by the +.Xr adduser 8 +utility when invoked with the +.Fl C +command-line option. +It is not meant to be edited by hand. +.Pp +The +.Pa /etc/adduser.conf +file is used to pre-set certain configuration options for +the +.Xr adduser 8 +utility. +When +.Xr adduser 8 +is invoked, it will check to see if this file exists, and +if so, the configuration will be used or offered as the +default settings. +The +.Nm +file offers three types of configuration: +.Bl -bullet +.It +Default settings offered by +.Xr adduser 8 . +These options are specified in the configuration file and offered +as the default during every invocation of the +.Xr adduser 8 +utility. +.It +Configuration options which can be set in +.Nm , +but overridden by passing a flag to +.Xr adduser 8 . +.It +Configuration supported by +.Xr adduser 8 +but not offered by a flag or during initial invocation. +.El +.Pp +In the first case, these options can be set in +.Nm +but will still be offered when +.Xr adduser 8 +is invoked. +In the second case, +.Xr adduser 8 +will read the configuration data unless a flag +has been passed to override it. +For example, the +.Va defaultshell +option. +In the third case, the configuration will be utilized, but the +user will never be prompted to modify the default setting by +either a flag or an +.Xr adduser 8 +prompt. +For example, the +.Va upwexpire +setting. +.Pp +The following configuration options can be set in +.Nm : +.Bl -tag -width ".Va defaultgroups" -offset indent +.It Va defaultLgroup +The default group new users will be added to. +.It Va defaultclass +The default class to place users in as described in +.Xr login.conf 5 . +.It Va defaultgroups +This option is used to specify what other groups the new account +should be added to. +.It Va passwdtype +May be one of +.Cm no , none , random , +or +.Cm yes , +as described in +.Xr adduser 8 . +As such, the text is not duplicated here and may be +read in +.Xr adduser 8 . +.It Va homeprefix +The default home directory prefix, usually +.Pa /home . +.It Va defaultshell +The user's default shell which may be any of the shells listed in +.Xr shells 5 . +.It Va udotdir +Defines the location of the default shell and environment +configuration files. +.It Va msgfile +Location of the default new user message file. +This message will be sent to all new users if specified +here or at the +.Xr adduser 8 +prompt. +.It Va disableflag +The default message enclosed in brackets for the +lock account prompt. +.It Va upwexpire +The default password expiration time. +Format of the date is either a +.Ux +time in decimal, or a date in +.Sm off +.Ar dd No - Ar mmm No - Ar yy Op Ar yy +.Sm on +format, where +.Ar dd +is the day, +.Ar mmm +is the month in either numeric or +alphabetic format, and +.Ar yy Ns Op Ar yy +is either a two or four digit year. +This option also accepts a relative date in the form of +.Sm off +.Ar n Op Ar m h d w o y +.Sm on +where +.Ar n +is a decimal, octal (leading 0) or hexadecimal (leading 0x) digit +followed by the number of Minutes, Hours, Days, Weeks, Months or +Years from the current date at +which the expiration time is to be set. +.It Va uexpire +The default account expire time. +The format is similar to the +.Va upwexpire +option. +.It Va ugecos +The default information to be held in the GECOS field of +.Pa /etc/master.passwd . +.It Va uidstart +The default user ID setting. +This must be a number above 1000 and fewer than 65534. +.El +.Sh EXAMPLES +The following is an example +.Nm +file created with the +.Fl C +.Xr adduser 8 +flag and modified. +.Bd -literal -offset indent +# Configuration file for adduser(8). +# NOTE: only *some* variables are saved. +# Last Modified on Fri Mar 30 14:04:05 EST 2004. + +defaultLgroup= +defaultclass= +defaultgroups= +passwdtype=yes +homeprefix=/home +defaultshell=/bin/csh +udotdir=/usr/share/skel +msgfile=/etc/adduser.msg +disableflag= +upwexpire=91d # Expire passwords 91 days after creation. +.Ed +.Sh SEE ALSO +.Xr group 5 , +.Xr passwd 5 , +.Xr adduser 8 , +.Xr pw 8 , +.Xr rmuser 8 +.Sh HISTORY +The +.Nm +manual page first appeared in +.Fx 5.3 . +.Sh AUTHORS +This manual page was written by +.An Tom Rhodes Aq Mt trhodes@FreeBSD.org . +.Sh BUGS +The internal variables documented here may change without notice. +Do not rely on them. +To modify this file invoke +.Xr adduser 8 +with the +.Fl C +option instead. diff --git a/usr.sbin/adduser/adduser.sh b/usr.sbin/adduser/adduser.sh new file mode 100644 index 0000000..4b0a6f6 --- /dev/null +++ b/usr.sbin/adduser/adduser.sh @@ -0,0 +1,1051 @@ +#!/bin/sh +# +# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Email: Mike Makonnen <mtm@FreeBSD.Org> +# +# $FreeBSD$ +# + +# err msg +# Display $msg on stderr, unless we're being quiet. +# +err() { + if [ -z "$quietflag" ]; then + echo 1>&2 ${THISCMD}: ERROR: $* + fi +} + +# info msg +# Display $msg on stdout, unless we're being quiet. +# +info() { + if [ -z "$quietflag" ]; then + echo ${THISCMD}: INFO: $* + fi +} + +# get_nextuid +# Output the value of $_uid if it is available for use. If it +# is not, output the value of the next higher uid that is available. +# If a uid is not specified, output the first available uid, as indicated +# by pw(8). +# +get_nextuid () { + _uid=$1 + _nextuid= + + if [ -z "$_uid" ]; then + _nextuid="`${PWCMD} usernext | cut -f1 -d:`" + else + while : ; do + ${PWCMD} usershow $_uid > /dev/null 2>&1 + if [ ! "$?" -eq 0 ]; then + _nextuid=$_uid + break + fi + _uid=$(($_uid + 1)) + done + fi + echo $_nextuid +} + +# show_usage +# Display usage information for this utility. +# +show_usage() { + echo "usage: ${THISCMD} [options]" + echo " options may include:" + echo " -C save to the configuration file only" + echo " -D do not attempt to create the home directory" + echo " -E disable this account after creation" + echo " -G additional groups to add accounts to" + echo " -L login class of the user" + echo " -M file permission for home directory" + echo " -N do not read configuration file" + echo " -S a nonexistent shell is not an error" + echo " -d home directory" + echo " -f file from which input will be received" + echo " -g default login group" + echo " -h display this usage message" + echo " -k path to skeleton home directory" + echo " -m user welcome message file" + echo " -q absolute minimal user feedback" + echo " -s shell" + echo " -u uid to start at" + echo " -w password type: no, none, yes or random" +} + +# valid_shells +# Outputs a list of valid shells from /etc/shells. Only the +# basename of the shell is output. +# +valid_shells() { + _prefix= + cat ${ETCSHELLS} | + while read _path _junk ; do + case $_path in + \#*|'') + ;; + *) + echo -n "${_prefix}`basename $_path`" + _prefix=' ' + ;; + esac + done + + # /usr/sbin/nologin is a special case + [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}" +} + +# fullpath_from_shell shell +# Given $shell, which is either the full path to a shell or +# the basename component of a valid shell, get the +# full path to the shell from the /etc/shells file. +# +fullpath_from_shell() { + _shell=$1 + [ -z "$_shell" ] && return 1 + + # /usr/sbin/nologin is a special case; it needs to be handled + # before the cat | while loop, since a 'return' from within + # a subshell will not terminate the function's execution, and + # the path to the nologin shell might be printed out twice. + # + if [ "$_shell" = "${NOLOGIN}" -o \ + "$_shell" = "${NOLOGIN_PATH}" ]; then + echo ${NOLOGIN_PATH} + return 0; + fi + + cat ${ETCSHELLS} | + while read _path _junk ; do + case "$_path" in + \#*|'') + ;; + *) + if [ "$_path" = "$_shell" -o \ + "`basename $_path`" = "$_shell" ]; then + echo $_path + return 0 + fi + ;; + esac + done + + return 1 +} + +# shell_exists shell +# If the given shell is listed in ${ETCSHELLS} or it is +# the nologin shell this function will return 0. +# Otherwise, it will return 1. If shell is valid but +# the path is invalid or it is not executable it +# will emit an informational message saying so. +# +shell_exists() { + _sh="$1" + _shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1" + + if ! eval $_shellchk; then + # The nologin shell is not listed in /etc/shells. + if [ "$_sh" != "${NOLOGIN_PATH}" ]; then + err "Invalid shell ($_sh) for user $username." + return 1 + fi + fi + ! [ -x "$_sh" ] && + info "The shell ($_sh) does not exist or is not executable." + + return 0 +} + +# save_config +# Save some variables to a configuration file. +# Note: not all script variables are saved, only those that +# it makes sense to save. +# +save_config() { + echo "# Configuration file for adduser(8)." > ${ADDUSERCONF} + echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF} + echo "# Last Modified on `${DATECMD}`." >> ${ADDUSERCONF} + echo '' >> ${ADDUSERCONF} + echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF} + echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF} + echo "defaultclass=$uclass" >> ${ADDUSERCONF} + echo "defaultgroups=$ugroups" >> ${ADDUSERCONF} + echo "passwdtype=$passwdtype" >> ${ADDUSERCONF} + echo "homeprefix=$homeprefix" >> ${ADDUSERCONF} + echo "defaultshell=$ushell" >> ${ADDUSERCONF} + echo "udotdir=$udotdir" >> ${ADDUSERCONF} + echo "msgfile=$msgfile" >> ${ADDUSERCONF} + echo "disableflag=$disableflag" >> ${ADDUSERCONF} + echo "uidstart=$uidstart" >> ${ADDUSERCONF} +} + +# add_user +# Add a user to the user database. If the user chose to send a welcome +# message or lock the account, do so. +# +add_user() { + + # Is this a configuration run? If so, don't modify user database. + # + if [ -n "$configflag" ]; then + save_config + return + fi + + _uid= + _name= + _comment= + _gecos= + _home= + _group= + _grouplist= + _shell= + _class= + _dotdir= + _expire= + _pwexpire= + _passwd= + _upasswd= + _passwdmethod= + + _name="-n '$username'" + [ -n "$uuid" ] && _uid='-u "$uuid"' + [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"' + [ -n "$ugroups" ] && _grouplist='-G "$ugroups"' + [ -n "$ushell" ] && _shell='-s "$ushell"' + [ -n "$uclass" ] && _class='-L "$uclass"' + [ -n "$ugecos" ] && _comment='-c "$ugecos"' + [ -n "$udotdir" ] && _dotdir='-k "$udotdir"' + [ -n "$uexpire" ] && _expire='-e "$uexpire"' + [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"' + if [ -z "$Dflag" -a -n "$uhome" ]; then + # The /nonexistent home directory is special. It + # means the user has no home directory. + if [ "$uhome" = "$NOHOME" ]; then + _home='-d "$uhome"' + else + # Use home directory permissions if specified + if [ -n "$uhomeperm" ]; then + _home='-m -d "$uhome" -M "$uhomeperm"' + else + _home='-m -d "$uhome"' + fi + fi + elif [ -n "$Dflag" -a -n "$uhome" ]; then + _home='-d "$uhome"' + fi + case $passwdtype in + no) + _passwdmethod="-w no" + _passwd="-h -" + ;; + yes) + # Note on processing the password: The outer double quotes + # make literal everything except ` and \ and $. + # The outer single quotes make literal ` and $. + # We can ensure the \ isn't treated specially by specifying + # the -r switch to the read command used to obtain the input. + # + _passwdmethod="-w yes" + _passwd="-h 0" + _upasswd='echo "$upass" |' + ;; + none) + _passwdmethod="-w none" + ;; + random) + _passwdmethod="-w random" + ;; + esac + + _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment" + _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd" + _pwcmd="$_pwcmd $_expire $_pwexpire" + + if ! _output=`eval $_pwcmd` ; then + err "There was an error adding user ($username)." + return 1 + else + info "Successfully added ($username) to the user database." + if [ "random" = "$passwdtype" ]; then + randompass="$_output" + info "Password for ($username) is: $randompass" + fi + fi + + if [ -n "$disableflag" ]; then + if ${PWCMD} lock $username ; then + info "Account ($username) is locked." + else + info "Account ($username) could NOT be locked." + fi + fi + + _line= + _owner= + _perms= + if [ -n "$msgflag" ]; then + [ -r "$msgfile" ] && { + # We're evaluating the contents of an external file. + # Let's not open ourselves up for attack. _perms will + # be empty if it's writeable only by the owner. _owner + # will *NOT* be empty if the file is owned by root. + # + _dir="`dirname $msgfile`" + _file="`basename $msgfile`" + _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune` + _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune` + if [ -z "$_owner" -o -n "$_perms" ]; then + err "The message file ($msgfile) may be writeable only by root." + return 1 + fi + cat "$msgfile" | + while read _line ; do + eval echo "$_line" + done | ${MAILCMD} -s"Welcome" ${username} + info "Sent welcome message to ($username)." + } + fi +} + +# get_user +# Reads username of the account from standard input or from a global +# variable containing an account line from a file. The username is +# required. If this is an interactive session it will prompt in +# a loop until a username is entered. If it is batch processing from +# a file it will output an error message and return to the caller. +# +get_user() { + _input= + + # No need to take down user names if this is a configuration saving run. + [ -n "$configflag" ] && return + + while : ; do + if [ -z "$fflag" ]; then + echo -n "Username: " + read _input + else + _input="`echo "$fileline" | cut -f1 -d:`" + fi + + # There *must* be a username, and it must not exist. If + # this is an interactive session give the user an + # opportunity to retry. + # + if [ -z "$_input" ]; then + err "You must enter a username!" + [ -z "$fflag" ] && continue + fi + ${PWCMD} usershow $_input > /dev/null 2>&1 + if [ "$?" -eq 0 ]; then + err "User exists!" + [ -z "$fflag" ] && continue + fi + break + done + username="$_input" +} + +# get_gecos +# Reads extra information about the user. Can be used both in interactive +# and batch (from file) mode. +# +get_gecos() { + _input= + + # No need to take down additional user information for a configuration run. + [ -n "$configflag" ] && return + + if [ -z "$fflag" ]; then + echo -n "Full name: " + read _input + else + _input="`echo "$fileline" | cut -f7 -d:`" + fi + ugecos="$_input" +} + +# get_shell +# Get the account's shell. Works in interactive and batch mode. It +# accepts either the base name of the shell or the full path. +# If an invalid shell is entered it will simply use the default shell. +# +get_shell() { + _input= + _fullpath= + ushell="$defaultshell" + + # Make sure the current value of the shell is a valid one + if [ -z "$Sflag" ]; then + if ! shell_exists $ushell ; then + info "Using default shell ${defaultshell}." + ushell="$defaultshell" + fi + fi + + if [ -z "$fflag" ]; then + echo -n "Shell ($shells) [`basename $ushell`]: " + read _input + else + _input="`echo "$fileline" | cut -f9 -d:`" + fi + if [ -n "$_input" ]; then + if [ -n "$Sflag" ]; then + ushell="$_input" + else + _fullpath=`fullpath_from_shell $_input` + if [ -n "$_fullpath" ]; then + ushell="$_fullpath" + else + err "Invalid shell ($_input) for user $username." + info "Using default shell ${defaultshell}." + ushell="$defaultshell" + fi + fi + fi +} + +# get_homedir +# Reads the account's home directory. Used both with interactive input +# and batch input. +# +get_homedir() { + _input= + if [ -z "$fflag" ]; then + echo -n "Home directory [${homeprefix}/${username}]: " + read _input + else + _input="`echo "$fileline" | cut -f8 -d:`" + fi + + if [ -n "$_input" ]; then + uhome="$_input" + # if this is a configuration run, then user input is the home + # directory prefix. Otherwise it is understood to + # be $prefix/$user + # + [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome" + else + uhome="${homeprefix}/${username}" + fi +} + +# get_homeperm +# Reads the account's home directory permissions. +# +get_homeperm() { + uhomeperm=$defaultHomePerm + _input= + _prompt= + + if [ -n "$uhomeperm" ]; then + _prompt="Home directory permissions [${uhomeperm}]: " + else + _prompt="Home directory permissions (Leave empty for default): " + fi + if [ -z "$fflag" ]; then + echo -n "$_prompt" + read _input + fi + + if [ -n "$_input" ]; then + uhomeperm="$_input" + fi +} + +# get_uid +# Reads a numeric userid in an interactive or batch session. Automatically +# allocates one if it is not specified. +# +get_uid() { + uuid=${uidstart} + _input= + _prompt= + + if [ -n "$uuid" ]; then + uuid=`get_nextuid $uuid` + _prompt="Uid [$uuid]: " + else + _prompt="Uid (Leave empty for default): " + fi + if [ -z "$fflag" ]; then + echo -n "$_prompt" + read _input + else + _input="`echo "$fileline" | cut -f2 -d:`" + fi + + [ -n "$_input" ] && uuid=$_input + uuid=`get_nextuid $uuid` + uidstart=$uuid +} + +# get_class +# Reads login class of account. Can be used in interactive or batch mode. +# +get_class() { + uclass="$defaultclass" + _input= + _class=${uclass:-"default"} + + if [ -z "$fflag" ]; then + echo -n "Login class [$_class]: " + read _input + else + _input="`echo "$fileline" | cut -f4 -d:`" + fi + + [ -n "$_input" ] && uclass="$_input" +} + +# get_logingroup +# Reads user's login group. Can be used in both interactive and batch +# modes. The specified value can be a group name or its numeric id. +# This routine leaves the field blank if nothing is provided and +# a default login group has not been set. The pw(8) command +# will then provide a login group with the same name as the username. +# +get_logingroup() { + ulogingroup="$defaultLgroup" + _input= + + if [ -z "$fflag" ]; then + echo -n "Login group [${ulogingroup:-$username}]: " + read _input + else + _input="`echo "$fileline" | cut -f3 -d:`" + fi + + # Pw(8) will use the username as login group if it's left empty + [ -n "$_input" ] && ulogingroup="$_input" +} + +# get_groups +# Read additional groups for the user. It can be used in both interactive +# and batch modes. +# +get_groups() { + ugroups="$defaultgroups" + _input= + _group=${ulogingroup:-"${username}"} + + if [ -z "$configflag" ]; then + [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username" + [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: " + else + [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: " + fi + read _input + + [ -n "$_input" ] && ugroups="$_input" +} + +# get_expire_dates +# Read expiry information for the account and also for the password. This +# routine is used only from batch processing mode. +# +get_expire_dates() { + upwexpire="`echo "$fileline" | cut -f5 -d:`" + uexpire="`echo "$fileline" | cut -f6 -d:`" +} + +# get_password +# Read the password in batch processing mode. The password field matters +# only when the password type is "yes" or "random". If the field is empty and the +# password type is "yes", then it assumes the account has an empty passsword +# and changes the password type accordingly. If the password type is "random" +# and the password field is NOT empty, then it assumes the account will NOT +# have a random password and set passwdtype to "yes." +# +get_password() { + # We may temporarily change a password type. Make sure it's changed + # back to whatever it was before we process the next account. + # + [ -n "$savedpwtype" ] && { + passwdtype=$savedpwtype + savedpwtype= + } + + # There may be a ':' in the password + upass=${fileline#*:*:*:*:*:*:*:*:*:} + + if [ -z "$upass" ]; then + case $passwdtype in + yes) + # if it's empty, assume an empty password + passwdtype=none + savedpwtype=yes + ;; + esac + else + case $passwdtype in + random) + passwdtype=yes + savedpwtype=random + ;; + esac + fi +} + +# input_from_file +# Reads a line of account information from standard input and +# adds it to the user database. +# +input_from_file() { + _field= + + while read -r fileline ; do + case "$fileline" in + \#*|'') + ;; + *) + get_user || continue + get_gecos + get_uid + get_logingroup + get_class + get_shell + get_homedir + get_homeperm + get_password + get_expire_dates + ugroups="$defaultgroups" + + add_user + ;; + esac + done +} + +# input_interactive +# Prompts for user information interactively, and commits to +# the user database. +# +input_interactive() { + _disable= + _pass= + _passconfirm= + _random="no" + _emptypass="no" + _usepass="yes" + _logingroup_ok="no" + _groups_ok="no" + case $passwdtype in + none) + _emptypass="yes" + _usepass="yes" + ;; + no) + _usepass="no" + ;; + random) + _random="yes" + ;; + esac + + get_user + get_gecos + get_uid + + # The case where group = user is handled elsewhere, so + # validate any other groups the user is invited to. + until [ "$_logingroup_ok" = yes ]; do + get_logingroup + _logingroup_ok=yes + if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then + if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then + echo "Group $ulogingroup does not exist!" + _logingroup_ok=no + fi + fi + done + until [ "$_groups_ok" = yes ]; do + get_groups + _groups_ok=yes + for i in $ugroups; do + if [ "$username" != "$i" ]; then + if ! ${PWCMD} show group $i > /dev/null 2>&1; then + echo "Group $i does not exist!" + _groups_ok=no + fi + fi + done + done + + get_class + get_shell + get_homedir + get_homeperm + + while : ; do + echo -n "Use password-based authentication? [$_usepass]: " + read _input + [ -z "$_input" ] && _input=$_usepass + case $_input in + [Nn][Oo]|[Nn]) + passwdtype="no" + ;; + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + while : ; do + echo -n "Use an empty password? (yes/no) [$_emptypass]: " + read _input + [ -n "$_input" ] && _emptypass=$_input + case $_emptypass in + [Nn][Oo]|[Nn]) + echo -n "Use a random password? (yes/no) [$_random]: " + read _input + [ -n "$_input" ] && _random="$_input" + case $_random in + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + passwdtype="random" + break + ;; + esac + passwdtype="yes" + [ -n "$configflag" ] && break + trap 'stty echo; exit' 0 1 2 3 15 + stty -echo + echo -n "Enter password: " + read -r upass + echo'' + echo -n "Enter password again: " + read -r _passconfirm + echo '' + stty echo + # if user entered a blank password + # explicitly ask again. + [ -z "$upass" -a -z "$_passconfirm" ] \ + && continue + ;; + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + passwdtype="none" + break; + ;; + *) + # invalid answer; repeat the loop + continue + ;; + esac + if [ "$upass" != "$_passconfirm" ]; then + echo "Passwords did not match!" + continue + fi + break + done + ;; + *) + # invalid answer; repeat loop + continue + ;; + esac + break; + done + _disable=${disableflag:-"no"} + while : ; do + echo -n "Lock out the account after creation? [$_disable]: " + read _input + [ -z "$_input" ] && _input=$_disable + case $_input in + [Nn][Oo]|[Nn]) + disableflag= + ;; + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + disableflag=yes + ;; + *) + # invalid answer; repeat loop + continue + ;; + esac + break + done + + # Display the information we have so far and prompt to + # commit it. + # + _disable=${disableflag:-"no"} + [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username + case $passwdtype in + yes) + _pass='*****' + ;; + no) + _pass='<disabled>' + ;; + none) + _pass='<blank>' + ;; + random) + _pass='<random>' + ;; + esac + [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass" + [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype" + [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos" + [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid" + printf "%-10s : %s\n" "Class" "$uclass" + printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups" + printf "%-10s : %s\n" "Home" "$uhome" + printf "%-10s : %s\n" "Home Mode" "$uhomeperm" + printf "%-10s : %s\n" "Shell" "$ushell" + printf "%-10s : %s\n" "Locked" "$_disable" + while : ; do + echo -n "OK? (yes/no): " + read _input + case $_input in + [Nn][Oo]|[Nn]) + return 1 + ;; + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + add_user + ;; + *) + continue + ;; + esac + break + done + return 0 +} + +#### END SUBROUTINE DEFINITION #### + +THISCMD=`/usr/bin/basename $0` +DEFAULTSHELL=/bin/sh +ADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}" +PWCMD="${PWCMD:-/usr/sbin/pw}" +MAILCMD="${MAILCMD:-mail}" +ETCSHELLS="${ETCSHELLS:-/etc/shells}" +NOHOME="/nonexistent" +NOLOGIN="nologin" +NOLOGIN_PATH="/usr/sbin/nologin" +GREPCMD="/usr/bin/grep" +DATECMD="/bin/date" + +# Set default values +# +username= +uuid= +uidstart= +ugecos= +ulogingroup= +uclass= +uhome= +uhomeperm= +upass= +ushell= +udotdir=/usr/share/skel +ugroups= +uexpire= +upwexpire= +shells="`valid_shells`" +passwdtype="yes" +msgfile=/etc/adduser.msg +msgflag= +quietflag= +configflag= +fflag= +infile= +disableflag= +Dflag= +Sflag= +readconfig="yes" +homeprefix="/home" +randompass= +fileline= +savedpwtype= +defaultclass= +defaultLgroup= +defaultgroups= +defaultshell="${DEFAULTSHELL}" +defaultHomePerm= + +# Make sure the user running this program is root. This isn't a security +# measure as much as it is a useful method of reminding the user to +# 'su -' before he/she wastes time entering data that won't be saved. +# +procowner=${procowner:-`/usr/bin/id -u`} +if [ "$procowner" != "0" ]; then + err 'you must be the super-user (uid 0) to use this utility.' + exit 1 +fi + +# Override from our conf file +# Quickly go through the commandline line to see if we should read +# from our configuration file. The actual parsing of the commandline +# arguments happens after we read in our configuration file (commandline +# should override configuration file). +# +for _i in $* ; do + if [ "$_i" = "-N" ]; then + readconfig= + break; + fi +done +if [ -n "$readconfig" ]; then + # On a long-lived system, the first time this script is run it + # will barf upon reading the configuration file for its perl predecessor. + if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then + [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1 + fi +fi + +# Process command-line options +# +for _switch ; do + case $_switch in + -L) + defaultclass="$2" + shift; shift + ;; + -C) + configflag=yes + shift + ;; + -D) + Dflag=yes + shift + ;; + -E) + disableflag=yes + shift + ;; + -k) + udotdir="$2" + shift; shift + ;; + -f) + [ "$2" != "-" ] && infile="$2" + fflag=yes + shift; shift + ;; + -g) + defaultLgroup="$2" + shift; shift + ;; + -G) + defaultgroups="$2" + shift; shift + ;; + -h) + show_usage + exit 0 + ;; + -d) + homeprefix="$2" + shift; shift + ;; + -m) + case "$2" in + [Nn][Oo]) + msgflag= + ;; + *) + msgflag=yes + msgfile="$2" + ;; + esac + shift; shift + ;; + -M) + defaultHomePerm=$2 + shift; shift + ;; + -N) + readconfig= + shift + ;; + -w) + case "$2" in + no|none|random|yes) + passwdtype=$2 + ;; + *) + show_usage + exit 1 + ;; + esac + shift; shift + ;; + -q) + quietflag=yes + shift + ;; + -s) + defaultshell="`fullpath_from_shell $2`" + shift; shift + ;; + -S) + Sflag=yes + shift + ;; + -u) + uidstart=$2 + shift; shift + ;; + esac +done + +# If the -f switch was used, get input from a file. Otherwise, +# this is an interactive session. +# +if [ -n "$fflag" ]; then + if [ -z "$infile" ]; then + input_from_file + elif [ -n "$infile" ]; then + if [ -r "$infile" ]; then + input_from_file < $infile + else + err "File ($infile) is unreadable or does not exist." + fi + fi +else + input_interactive + while : ; do + if [ -z "$configflag" ]; then + echo -n "Add another user? (yes/no): " + else + echo -n "Re-edit the default configuration? (yes/no): " + fi + read _input + case $_input in + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + uidstart=`get_nextuid $uidstart` + input_interactive + continue + ;; + [Nn][Oo]|[Nn]) + echo "Goodbye!" + ;; + *) + continue + ;; + esac + break + done +fi diff --git a/usr.sbin/adduser/rmuser.8 b/usr.sbin/adduser/rmuser.8 new file mode 100644 index 0000000..e24d5ee --- /dev/null +++ b/usr.sbin/adduser/rmuser.8 @@ -0,0 +1,210 @@ +.\" Copyright 1995, 1996, 1997 +.\" Guy Helmer, Ames, Iowa 50014. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer as +.\" the first lines of this file unmodified. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY GUY HELMER ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd May 10, 2002 +.Dt RMUSER 8 +.Os +.Sh NAME +.Nm rmuser +.Nd remove users from the system +.Sh SYNOPSIS +.Nm +.Op Fl yv +.Op Fl f Ar file +.Op Ar username ... +.Sh DESCRIPTION +The +.Nm +utility removes one or more users submitted on the command line +or from a file. +In removing a user from the system, this utility: +.Bl -enum +.It +Removes the user's +.Xr crontab 1 +entry (if any). +.It +Removes any +.Xr at 1 +jobs belonging to the user. +.It +Sends a +.Dv SIGKILL +signal to all processes owned by the user. +.It +Removes the user from the system's local password file. +.It +Removes the user's home directory (if it is owned by the user), +including handling of symbolic links in the path to the actual home +directory. +.It +Removes the incoming mail and POP daemon mail files belonging to the +user from +.Pa /var/mail . +.It +Removes all files owned by the user from +.Pa /tmp , /var/tmp , +and +.Pa /var/tmp/vi.recover . +.It +Removes the username from all groups to which it belongs in +.Pa /etc/group . +(If a group becomes empty and the group name is the same as the username, +the group is removed; this complements +.Xr adduser 8 Ns 's +per-user unique groups.) +.It +Removes all message queues, shared memory segments and +semaphores owned by the user. +.El +.Pp +The +.Nm +utility refuses to remove users whose UID is 0 (typically root), since +certain actions (namely, killing all the user's processes, and perhaps +removing the user's home directory) would cause damage to a running system. +If it is necessary to remove a user whose UID is 0, see +.Xr vipw 8 +for information on directly editing the password file. +.Pp +If +.Nm +was not invoked with the +.Fl y +option, it will +show the selected user's password file entry and ask for confirmation +that the user be removed. +It will then ask for confirmation to delete +the user's home directory. +If the answer is in the affirmative, the home +directory and any files and subdirectories under it will be deleted only if +they are owned by the user. +See +.Xr pw 8 +for more details. +.Pp +As +.Nm +operates, it informs the user regarding the current activity. +If any +errors occur, they are posted to standard error and, if it is possible for +.Nm +to continue, it will. +.Pp +The options are as follows: +.Bl -tag -width ".Ar username" +.It Fl f Ar file +The +.Nm +utility will get a list of users to be removed from +.Ar file , +which will contain one user per line. +Anything following a hash mark +.Pq Ql # , +including the hash mark itself, is considered a comment and will not +be processed. +If the file is owned by anyone other than a user with +UID 0, or is writable by anyone other than the owner, +.Nm +will refuse to continue. +.It Fl y +Implicitly answer +.Dq Li yes +to any and all prompts. +Currently, this includes +prompts on whether to remove the specified user and whether to remove +the home directory. +This option requires that either the +.Fl f +option be used, or one or more user names be given as command line +arguments. +.It Fl v +Enable verbose mode. +Normally, +the output includes one line per removed user; +however, +with this option +.Nm +will be much more chatty about the steps taken. +.It Ar username +Identifies one or more users to be removed; if not present, +.Nm +interactively asks for one or more users to be removed. +.El +.Sh FILES +.Bl -tag -width "Pa /etc/master.passwd" -compact +.It Pa /etc/master.passwd +.It Pa /etc/passwd +.It Pa /etc/group +.It Pa /etc/spwd.db +.It Pa /etc/pwd.db +.El +.Sh SEE ALSO +.Xr at 1 , +.Xr chpass 1 , +.Xr crontab 1 , +.Xr finger 1 , +.Xr passwd 1 , +.Xr group 5 , +.Xr passwd 5 , +.Xr adduser 8 , +.Xr pw 8 , +.Xr pwd_mkdb 8 , +.Xr vipw 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 2.2 . +.Sh BUGS +The +.Nm +utility does not comprehensively search the file system for all files +owned by the removed user and remove them; to do so on a system +of any size is prohibitively slow and I/O intensive. +It is also unable to remove symbolic links that were created by the +user in +.Pa /tmp +or +.Pa /var/tmp , +as symbolic links on +.Bx 4.4 +file systems do not contain information +as to who created them. +Also, there may be other files created in +.Pa /var/mail +other than +.Pa /var/mail/ Ns Ar username +and +.Pa /var/mail/.pop. Ns Ar username +that are not owned by the removed user but should be removed. +.Pp +The +.Nm +utility has no knowledge of YP/NIS, and it operates only on the +local password file. diff --git a/usr.sbin/adduser/rmuser.sh b/usr.sbin/adduser/rmuser.sh new file mode 100644 index 0000000..6b09225 --- /dev/null +++ b/usr.sbin/adduser/rmuser.sh @@ -0,0 +1,361 @@ +#!/bin/sh +# +# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# Email: Mike Makonnen <mtm@FreeBSD.Org> +# +# $FreeBSD$ +# + +ATJOBDIR="/var/at/jobs" +CRONJOBDIR="/var/cron/tabs" +MAILSPOOL="/var/mail" +SIGKILL="-KILL" +TEMPDIRS="/tmp /var/tmp" +THISCMD=`/usr/bin/basename $0` +PWCMD="${PWCMD:-/usr/sbin/pw}" + +# err msg +# Display $msg on stderr. +# +err() { + echo 1>&2 ${THISCMD}: $* +} + +# verbose +# Returns 0 if verbose mode is set, 1 if it is not. +# +verbose() { + [ -n "$vflag" ] && return 0 || return 1 +} + +# rm_files login +# Removes files or empty directories belonging to $login from various +# temporary directories. +# +rm_files() { + # The argument is required + [ -n $1 ] && login=$1 || return + + totalcount=0 + for _dir in ${TEMPDIRS} ; do + filecount=0 + if [ ! -d $_dir ]; then + err "$_dir is not a valid directory." + continue + fi + verbose && echo -n "Removing files owned by ($login) in $_dir:" + filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print | + wc -l | sed 's/ *//'` + verbose && echo " $filecount removed." + totalcount=$(($totalcount + $filecount)) + done + ! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)" +} + +# rm_mail login +# Removes unix mail and pop daemon files belonging to the user +# specified in the $login argument. +# +rm_mail() { + # The argument is required + [ -n $1 ] && login=$1 || return + + verbose && echo -n "Removing mail spool(s) for ($login):" + if [ -f ${MAILSPOOL}/$login ]; then + verbose && echo -n " ${MAILSPOOL}/$login" || + echo -n " mailspool" + rm ${MAILSPOOL}/$login + fi + if [ -f ${MAILSPOOL}/.${login}.pop ]; then + verbose && echo -n " ${MAILSPOOL}/.${login}.pop" || + echo -n " pop3" + rm ${MAILSPOOL}/.${login}.pop + fi + verbose && echo '.' +} + +# kill_procs login +# Send a SIGKILL to all processes owned by $login. +# +kill_procs() { + # The argument is required + [ -n $1 ] && login=$1 || return + + verbose && echo -n "Terminating all processes owned by ($login):" + killcount=0 + proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'` + for _pid in $proclist ; do + kill 2>/dev/null ${SIGKILL} $_pid + killcount=$(($killcount + 1)) + done + verbose && echo " ${SIGKILL} signal sent to $killcount processes." + ! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})" +} + +# rm_at_jobs login +# Remove at (1) jobs belonging to $login. +# +rm_at_jobs() { + # The argument is required + [ -n $1 ] && login=$1 || return + + atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print` + jobcount=0 + verbose && echo -n "Removing at(1) jobs owned by ($login):" + for _atjob in $atjoblist ; do + rm -f $_atjob + jobcount=$(($jobcount + 1)) + done + verbose && echo " $jobcount removed." + ! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)" +} + +# rm_crontab login +# Removes crontab file belonging to user $login. +# +rm_crontab() { + # The argument is required + [ -n $1 ] && login=$1 || return + + verbose && echo -n "Removing crontab for ($login):" + if [ -f ${CRONJOBDIR}/$login ]; then + verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab" + rm -f ${CRONJOBDIR}/$login + fi + verbose && echo '.' +} + +# rm_ipc login +# Remove all IPC mechanisms which are owned by $login. +# +rm_ipc() { + verbose && echo -n "Removing IPC mechanisms" + for i in s m q; do + ipcs -$i | + awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' | + xargs -n 1 ipcrm -$i + done + verbose && echo '.' +} + +# rm_user login +# Remove user $login from the system. This subroutine makes use +# of the pw(8) command to remove a user from the system. The pw(8) +# command will remove the specified user from the user database +# and group file and remove any crontabs. His home +# directory will be removed if it is owned by him and contains no +# files or subdirectories owned by other users. Mail spool files will +# also be removed. +# +rm_user() { + # The argument is required + [ -n $1 ] && login=$1 || return + + verbose && echo -n "Removing user ($login)" + [ -n "$pw_rswitch" ] && { + verbose && echo -n " (including home directory)" + ! verbose && echo -n " home" + } + ! verbose && echo -n " passwd" + verbose && echo -n " from the system:" + ${PWCMD} userdel -n $login $pw_rswitch + verbose && echo ' Done.' +} + +# prompt_yesno msg +# Prompts the user with a $msg. The answer is expected to be +# yes, no, or some variation thereof. This subroutine returns 0 +# if the answer was yes, 1 if it was not. +# +prompt_yesno() { + # The argument is required + [ -n "$1" ] && msg="$1" || return + + while : ; do + echo -n "$msg" + read _ans + case $_ans in + [Nn][Oo]|[Nn]) + return 1 + ;; + [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) + return 0 + ;; + *) + ;; + esac + done +} + +# show_usage +# (no arguments) +# Display usage message. +# +show_usage() { + echo "usage: ${THISCMD} [-yv] [-f file] [user ...]" + echo " if the -y switch is used, either the -f switch or" + echo " one or more user names must be given" +} + +#### END SUBROUTINE DEFENITION #### + +ffile= +fflag= +procowner= +pw_rswitch= +userlist= +yflag= +vflag= + +procowner=`/usr/bin/id -u` +if [ "$procowner" != "0" ]; then + err 'you must be root (0) to use this utility.' + exit 1 +fi + +args=`getopt 2>/dev/null yvf: $*` +if [ "$?" != "0" ]; then + show_usage + exit 1 +fi +set -- $args +for _switch ; do + case $_switch in + -y) + yflag=1 + shift + ;; + -v) + vflag=1 + shift + ;; + -f) + fflag=1 + ffile="$2" + shift; shift + ;; + --) + shift + break + ;; + esac +done + +# Get user names from a file if the -f switch was used. Otherwise, +# get them from the commandline arguments. If we're getting it +# from a file, the file must be owned by and writable only by root. +# +if [ $fflag ]; then + _insecure=`find $ffile ! -user 0 -or -perm +0022` + if [ -n "$_insecure" ]; then + err "file ($ffile) must be owned by and writeable only by root." + exit 1 + fi + if [ -r "$ffile" ]; then + userlist=`cat $ffile | while read _user _junk ; do + case $_user in + \#*|'') + ;; + *) + echo -n "$userlist $_user" + ;; + esac + done` + fi +else + while [ $1 ] ; do + userlist="$userlist $1" + shift + done +fi + +# If the -y or -f switch has been used and the list of users to remove +# is empty it is a fatal error. Otherwise, prompt the user for a list +# of one or more user names. +# +if [ ! "$userlist" ]; then + if [ $fflag ]; then + err "($ffile) does not exist or does not contain any user names." + exit 1 + elif [ $yflag ]; then + show_usage + exit 1 + else + echo -n "Please enter one or more usernames: " + read userlist + fi +fi + +_user= +_uid= +for _user in $userlist ; do + # Make sure the name exists in the passwd database and that it + # does not have a uid of 0 + # + userrec=`pw 2>/dev/null usershow -n $_user` + if [ "$?" != "0" ]; then + err "user ($_user) does not exist in the password database." + continue + fi + _uid=`echo $userrec | awk -F: '{print $3}'` + if [ "$_uid" = "0" ]; then + err "user ($_user) has uid 0. You may not remove this user." + continue + fi + + # If the -y switch was not used ask for confirmation to remove the + # user and home directory. + # + if [ -z "$yflag" ]; then + echo "Matching password entry:" + echo + echo $userrec + echo + if ! prompt_yesno "Is this the entry you wish to remove? " ; then + continue + fi + _homedir=`echo $userrec | awk -F: '{print $9}'` + if prompt_yesno "Remove user's home directory ($_homedir)? "; then + pw_rswitch="-r" + fi + else + pw_rswitch="-r" + fi + + # Disable any further attempts to log into this account + ${PWCMD} 2>/dev/null lock $_user + + # Remove crontab, mail spool, etc. Then obliterate the user from + # the passwd and group database. + # + ! verbose && echo -n "Removing user ($_user):" + rm_crontab $_user + rm_at_jobs $_user + rm_ipc $_user + kill_procs $_user + rm_files $_user + rm_mail $_user + rm_user $_user + ! verbose && echo "." +done |