aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/adduser/rmuser.sh
blob: 6b092253b70918e65c4292dd3573559aa9bf9379 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
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