1#!/bin/sh 2# 3# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved. 4# 5# Redistribution and use in source and binary forms, with or without 6# modification, are permitted provided that the following conditions 7# are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 14# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24# 25# Email: Mike Makonnen <mtm@FreeBSD.Org> 26# 27# $FreeBSD: src/usr.sbin/adduser/rmuser.sh,v 1.11 2008/07/30 18:37:21 jhb Exp $ 28# 29 30ATJOBDIR="/var/at/jobs" 31CRONJOBDIR="/var/cron/tabs" 32MAILSPOOL="/var/mail" 33SIGKILL="-KILL" 34TEMPDIRS="/tmp /var/tmp" 35THISCMD=`/usr/bin/basename $0` 36PWCMD="${PWCMD:-/usr/sbin/pw}" 37 38# err msg 39# Display $msg on stderr. 40# 41err() { 42 echo 1>&2 ${THISCMD}: $* 43} 44 45# verbose 46# Returns 0 if verbose mode is set, 1 if it is not. 47# 48verbose() { 49 [ -n "$vflag" ] && return 0 || return 1 50} 51 52# rm_files login 53# Removes files or empty directories belonging to $login from various 54# temporary directories. 55# 56rm_files() { 57 # The argument is required 58 [ -n $1 ] && login=$1 || return 59 60 totalcount=0 61 for _dir in ${TEMPDIRS} ; do 62 filecount=0 63 if [ ! -d $_dir ]; then 64 err "$_dir is not a valid directory." 65 continue 66 fi 67 verbose && echo -n "Removing files owned by ($login) in $_dir:" 68 filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print | 69 wc -l | sed 's/ *//'` 70 verbose && echo " $filecount removed." 71 totalcount=$(($totalcount + $filecount)) 72 done 73 ! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)" 74} 75 76# rm_mail login 77# Removes unix mail and pop daemon files belonging to the user 78# specified in the $login argument. 79# 80rm_mail() { 81 # The argument is required 82 [ -n $1 ] && login=$1 || return 83 84 verbose && echo -n "Removing mail spool(s) for ($login):" 85 if [ -f ${MAILSPOOL}/$login ]; then 86 verbose && echo -n " ${MAILSPOOL}/$login" || 87 echo -n " mailspool" 88 rm ${MAILSPOOL}/$login 89 fi 90 if [ -f ${MAILSPOOL}/.${login}.pop ]; then 91 verbose && echo -n " ${MAILSPOOL}/.${login}.pop" || 92 echo -n " pop3" 93 rm ${MAILSPOOL}/.${login}.pop 94 fi 95 verbose && echo '.' 96} 97 98# kill_procs login 99# Send a SIGKILL to all processes owned by $login. 100# 101kill_procs() { 102 # The argument is required 103 [ -n $1 ] && login=$1 || return 104 105 verbose && echo -n "Terminating all processes owned by ($login):" 106 killcount=0 107 proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'` 108 for _pid in $proclist ; do 109 kill 2>/dev/null ${SIGKILL} $_pid 110 killcount=$(($killcount + 1)) 111 done 112 verbose && echo " ${SIGKILL} signal sent to $killcount processes." 113 ! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})" 114} 115 116# rm_at_jobs login 117# Remove at (1) jobs belonging to $login. 118# 119rm_at_jobs() { 120 # The argument is required 121 [ -n $1 ] && login=$1 || return 122 123 atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print` 124 jobcount=0 125 verbose && echo -n "Removing at(1) jobs owned by ($login):" 126 for _atjob in $atjoblist ; do 127 rm -f $_atjob 128 jobcount=$(($jobcount + 1)) 129 done 130 verbose && echo " $jobcount removed." 131 ! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)" 132} 133 134# rm_crontab login 135# Removes crontab file belonging to user $login. 136# 137rm_crontab() { 138 # The argument is required 139 [ -n $1 ] && login=$1 || return 140 141 verbose && echo -n "Removing crontab for ($login):" 142 if [ -f ${CRONJOBDIR}/$login ]; then 143 verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab" 144 rm -f ${CRONJOBDIR}/$login 145 fi 146 verbose && echo '.' 147} 148 149# rm_ipc login 150# Remove all IPC mechanisms which are owned by $login. 151# 152rm_ipc() { 153 verbose && echo -n "Removing IPC mechanisms" 154 for i in s m q; do 155 ipcs -$i | 156 awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' | 157 xargs -n 1 ipcrm -$i 158 done 159 verbose && echo '.' 160} 161 162# rm_user login 163# Remove user $login from the system. This subroutine makes use 164# of the pw(8) command to remove a user from the system. The pw(8) 165# command will remove the specified user from the user database 166# and group file and remove any crontabs. His home 167# directory will be removed if it is owned by him and contains no 168# files or subdirectories owned by other users. Mail spool files will 169# also be removed. 170# 171rm_user() { 172 # The argument is required 173 [ -n $1 ] && login=$1 || return 174 175 verbose && echo -n "Removing user ($login)" 176 [ -n "$pw_rswitch" ] && { 177 verbose && echo -n " (including home directory)" 178 ! verbose && echo -n " home" 179 } 180 ! verbose && echo -n " passwd" 181 verbose && echo -n " from the system:" 182 ${PWCMD} userdel -n $login $pw_rswitch 183 verbose && echo ' Done.' 184} 185 186# prompt_yesno msg 187# Prompts the user with a $msg. The answer is expected to be 188# yes, no, or some variation thereof. This subroutine returns 0 189# if the answer was yes, 1 if it was not. 190# 191prompt_yesno() { 192 # The argument is required 193 [ -n "$1" ] && msg="$1" || return 194 195 while : ; do 196 echo -n "$msg" 197 read _ans 198 case $_ans in 199 [Nn][Oo]|[Nn]) 200 return 1 201 ;; 202 [Yy][Ee][Ss]|[Yy][Ee]|[Yy]) 203 return 0 204 ;; 205 *) 206 ;; 207 esac 208 done 209} 210 211# show_usage 212# (no arguments) 213# Display usage message. 214# 215show_usage() { 216 echo "usage: ${THISCMD} [-yv] [-f file] [user ...]" 217 echo " if the -y switch is used, either the -f switch or" 218 echo " one or more user names must be given" 219} 220 221#### END SUBROUTINE DEFENITION #### 222 223procowner= 224pw_rswitch= 225ffile= 226fflag= 227vflag= 228yflag= 229 230procowner=`/usr/bin/id -u` 231if [ "$procowner" != "0" ]; then 232 err 'you must be root (0) to use this utility.' 233 exit 1 234fi 235 236while getopts :f:hvy opt; do 237 case $opt in 238 f) 239 fflag=1 240 ffile="$OPTARG" 241 ;; 242 h) 243 show_usage 244 exit 1 245 ;; 246 v) 247 vflag=1 248 ;; 249 y) 250 yflag=1 251 ;; 252 \?) 253 err "Invalid option -${OPTARG}" 254 show_usage 255 exit 1 256 ;; 257 :) 258 err "Option -${OPTARG} requires an argument" 259 show_usage 260 exit 1 261 ;; 262 esac 263done 264 265shift $((OPTIND - 1)) 266 267# Get user names from a file if the -f switch was used. Otherwise, 268# get them from the commandline arguments. If we're getting it 269# from a file, the file must be owned by and writable only by root. 270# 271if [ -n "$fflag" ]; then 272 _insecure=`find $ffile ! -user 0 -or -perm +0022` 273 if [ -n "$_insecure" ]; then 274 err "file ($ffile) must be owned by and writeable only by root." 275 exit 1 276 fi 277 if [ -r "$ffile" ]; then 278 userlist=`cat $ffile | while read _user _junk ; do 279 case $_user in 280 \#*|'') 281 ;; 282 *) 283 echo -n "$_user " 284 ;; 285 esac 286 done` 287 else 288 err "($ffile) does not exist or does not contain any user names." 289 exit 1 290 fi 291else 292 userlist="$@" 293fi 294 295# If the -y switch has been used and the list of users to remove 296# is empty it is a fatal error. Otherwise, prompt the user for a list 297# of one or more user names. 298# 299userlist=`echo "$userlist" | sed -e 's/[[:space:]]*$//'` 300if [ -z "$userlist" ]; then 301 if [ -n "$yflag" ]; then 302 show_usage 303 exit 1 304 else 305 echo -n "Please enter one or more user names: " 306 read userlist 307 fi 308fi 309 310_user= 311_uid= 312for _user in $userlist ; do 313 # Make sure the name exists in the passwd database and that it 314 # does not have a uid of 0 315 # 316 userrec=`${PWCMD} 2>/dev/null usershow -n $_user` 317 if [ "$?" != "0" ]; then 318 err "user ($_user) does not exist in the password database." 319 continue 320 fi 321 _uid=`echo $userrec | awk -F: '{print $3}'` 322 if [ "$_uid" = "0" ]; then 323 err "user ($_user) has uid 0. You may not remove this user." 324 continue 325 fi 326 327 # If the -y switch was not used ask for confirmation to remove the 328 # user and home directory. 329 # 330 if [ -z "$yflag" ]; then 331 echo "Matching password entry:" 332 echo 333 echo $userrec 334 echo 335 if ! prompt_yesno "Is this the entry you wish to remove? " ; then 336 continue 337 fi 338 _homedir=`echo $userrec | awk -F: '{print $9}'` 339 if prompt_yesno "Remove user's home directory ($_homedir)? "; then 340 pw_rswitch="-r" 341 fi 342 else 343 pw_rswitch="-r" 344 fi 345 346 # Disable any further attempts to log into this account 347 ${PWCMD} 2>/dev/null lock $_user 348 349 # Remove crontab, mail spool, etc. Then obliterate the user from 350 # the passwd and group database. 351 # 352 ! verbose && echo -n "Removing user ($_user):" 353 rm_crontab $_user 354 rm_at_jobs $_user 355 rm_ipc $_user 356 kill_procs $_user 357 rm_files $_user 358 rm_mail $_user 359 rm_user $_user 360 ! verbose && echo "." 361done 362