diff options
author | wosch <wosch@FreeBSD.org> | 1996-07-10 22:29:30 +0000 |
---|---|---|
committer | wosch <wosch@FreeBSD.org> | 1996-07-10 22:29:30 +0000 |
commit | d8e4fc26f823d23548c55eab345ac121fcb3a1d3 (patch) | |
tree | 35ecf530a0906493bf9823cfbb9cc459eb008a07 /share/examples | |
download | FreeBSD-src-d8e4fc26f823d23548c55eab345ac121fcb3a1d3.zip FreeBSD-src-d8e4fc26f823d23548c55eab345ac121fcb3a1d3.tar.gz |
import removeuser from Guy Helmer
see file TODO for known problems
Diffstat (limited to 'share/examples')
-rw-r--r-- | share/examples/removeuser/README.removeuser | 11 | ||||
-rw-r--r-- | share/examples/removeuser/TODO | 31 | ||||
-rw-r--r-- | share/examples/removeuser/removeuser.8 | 105 | ||||
-rw-r--r-- | share/examples/removeuser/removeuser.perl | 416 |
4 files changed, 563 insertions, 0 deletions
diff --git a/share/examples/removeuser/README.removeuser b/share/examples/removeuser/README.removeuser new file mode 100644 index 0000000..28d354c --- /dev/null +++ b/share/examples/removeuser/README.removeuser @@ -0,0 +1,11 @@ +# $Id$ + +Here lies removeuser(8) for FreeBSD 2.x. It will remove a user's +password entry, home directory, and incoming mail file from /var/mail. + +Install removeuser.perl into /usr/local/sbin, /usr/sbin, or where ever +you like; install removeuser.8 into /usr/local/man/man8 or where ever. +I suggest these commands to install it: + +install -c -o bin -g bin -m 555 removeuser.perl /usr/local/sbin/removeuser +install -c -o bin -g bin -m 444 removeuser.8 /usr/local/man/man8/removeuser.8 diff --git a/share/examples/removeuser/TODO b/share/examples/removeuser/TODO new file mode 100644 index 0000000..80b1071 --- /dev/null +++ b/share/examples/removeuser/TODO @@ -0,0 +1,31 @@ +# $Id: $ + +Deleting a user is a complex task: + +DONE: +- delete password entry +- delete user from group database (/etc/group), may be delete groups +- remove home dir +- remove mailbox +- delete crontab entries (/etc/crontab, /var/cron/allow, +/var/cron/deny, /var/cron/tabs/user) +- delete at(8) entries + +TODO: +- delete ppp password (/etc/ppp/*) +- delete slip entries (/etc/sliphome*) +- delete user from /etc/ftpusers +- check for other users with same uid (may be also for groups) +- remove WWW files (/~user), which are not in HOME located +- check other FS, $ find / -user user -print, delete suid/sgid bit, + remove files in /var/tmp +- delete user from /etc/inetd.conf, remove files which owned by user +- delete user from /etc/rc.local if the user start programs + +- delete mail aliases (/etc/aliases, may be /etc/sendmail.cf) +- delete user phone numbers in /etc/phones +- remove quota + +KNOWN BUGS: +- removeuser does not remove the HOME directory if the HOME + directory is a symbolic link. diff --git a/share/examples/removeuser/removeuser.8 b/share/examples/removeuser/removeuser.8 new file mode 100644 index 0000000..b17f557 --- /dev/null +++ b/share/examples/removeuser/removeuser.8 @@ -0,0 +1,105 @@ +.\" Copyright 1995, 1996 +.\" Guy Helmer, Madison, South Dakota 57042. 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Guy Helmer. +.\" 4. 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. +.\" +.\" $Id$ +.\" +.Dd June 28, 1996 +.Dt REMOVEUSER 8 +.Os +.Sh NAME +.Nm removeuser +.Nd remove users from the system +.Sh SYNOPSIS +.Nm removeuser +.Op Ar username +.Sh DESCRIPTION +The utility +.Nm removeuser +removes a user's +.Xr crontab 1 +entry (if any) and any +.Xr at 1 +jobs belonging to the user, +then removes a user from the system's local password file, removes +the user's home directory if it is owned by the user, and removes +the user's incoming mail file if it exists. The username is removed +from any groups to which it belongs in the file +.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 's +per-user unique groups). +.Pp +.Nm removeuser +politely refuses to remove users whose uid is 0 (typically root), since +it seemed like a good idea at the time +.Nm removeuser +was written. +.Pp +.Nm removeuser +shows the selected user's password file entry and asks for confirmation +that you wish to remove the user. If the user's home directory is owned +by the user (and not by any other user), +.Nm removeuser +asks whether you wish to remove the user's home directory and everything +below. +.Pp +Available options: +.Pp +.Bl -tag -width username +.It Ar \&username +Identifies the user to be removed; if not present, +.Nm removeuser +interactively asks for the user to be removed. +.Sh FILES +.Bl -tag -width /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 pwd_mkdb 8 , +.Xr vipw 8 +.Sh HISTORY +The +.Nm +command appeared in FreeBSD 2.1.5 + +.\" .Sh AUTHOR +.\" Guy Helmer, Madison, South Dakota diff --git a/share/examples/removeuser/removeuser.perl b/share/examples/removeuser/removeuser.perl new file mode 100644 index 0000000..a0fd742 --- /dev/null +++ b/share/examples/removeuser/removeuser.perl @@ -0,0 +1,416 @@ +#!/usr/bin/perl +# -*- perl -*- +# Copyright 1995, 1996 Guy Helmer, Madison, South Dakota 57042. +# 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. +# +# /usr/sbin/removeuser +# Perl script to remove users +# +# Guy Helmer <ghelmer@alpha.dsu.edu>, 06/30/96 +# +# $Id$ + +sub LOCK_SH {0x01;} +sub LOCK_EX {0x02;} +sub LOCK_NB {0x04;} +sub LOCK_UN {0x08;} +sub F_SETFD {2;} + +$ENV{"PATH"} = "/bin:/sbin:/usr/bin:/usr/sbin"; +umask(022); +$whoami = $0; +$passwd_file = "/etc/master.passwd"; +$new_passwd_file = "${passwd_file}.new.$$"; +$group_file = "/etc/group"; +$new_group_file = "${group_file}.new.$$"; +$mail_dir = "/var/mail"; +$crontab_dir = "/var/cron/tabs"; +$atjob_dir = "/var/at/jobs"; + +#$debug = 1; + +sub cleanup { + local($sig) = @_; + + print STDERR "Caught signal SIG$sig -- cleaning up.\n"; + &unlockpw; + if (-e $new_passwd_file) { + unlink $new_passwd_file; + } + exit(0); +} + +sub lockpw { + # Open the password file for reading + if (!open(MASTER_PW, "$passwd_file")) { + print STDERR "${whoami}: Error: Couldn't open ${passwd_file}: $!\n"; + exit(1); + } + # Set the close-on-exec flag just in case + fcntl(MASTER_PW, &F_SETFD, 1); + # Apply an advisory lock the password file + if (!flock(MASTER_PW, &LOCK_EX|&LOCK_NB)) { + print STDERR "Couldn't lock ${passwd_file}: $!\n"; + exit(1); + } +} + +sub unlockpw { + flock(MASTER_PW, &LOCK_UN); +} + +$SIG{'INT'} = 'cleanup'; +$SIG{'QUIT'} = 'cleanup'; +$SIG{'HUP'} = 'cleanup'; +$SIG{'TERM'} = 'cleanup'; + +if ($#ARGV > 0) { + print STDERR "usage: ${whoami} [username]\n"; + exit(1); +} + +if ($< != 0) { + print STDERR "${whoami}: Error: you must be root to use ${whoami}\n"; + exit(1); +} + +&lockpw; + +if ($#ARGV == 0) { + # Username was given as a parameter + $login_name = pop(@ARGV); +} else { + # Get the user name from the user + $login_name = &get_login_name; +} + +if (($pw_ent = &check_login_name($login_name)) eq '0') { + print STDERR "${whoami}: Error: User ${login_name} not in password database\n"; + &unlockpw; + exit 1; +} + +($name, $password, $uid, $gid, $class, $change, $expire, $gecos, $home_dir, + $shell) = split(/:/, $pw_ent); + +if ($uid == 0) { + print "${whoami}: Sorry, I'd rather not remove a user with a uid of 0.\n"; + &unlockpw; + exit 1; +} + +print "Matching password entry:\n\n$pw_ent\n\n"; + +$ans = &get_yn("Is this the entry you wish to remove? "); + +if ($ans eq 'N') { + print "User ${login_name} not removed.\n"; + &unlockpw; + exit 0; +} + +# +# Get owner of user's home directory; don't remove home dir if not +# owned by $login_name + +$remove_directory = 1; + +if (!(-d $home_dir)) { + print STDERR "${whoami}: Home ${home_dir} is not a directory, so it won't be removed\n"; + $remove_directory = 0; +} else { + $dir_owner = (stat($home_dir))[4]; # UID + if ($dir_owner != $uid) { + print STDERR "${whoami}: Home dir ${home_dir} is not owned by ${login_name} (uid ${dir_owner})\n"; + $remove_directory = 0; + } +} + +if ($remove_directory) { + $ans = &get_yn("Remove user's home directory ($home_dir)? "); + if ($ans eq 'N') { + $remove_directory = 0; + } +} + +#exit 0 if $debug; + +# +# Remove the user's crontab, if there is one +# (probably needs to be done before password databases are updated) + +if (-e "$crontab_dir/$login_name") { + print STDERR "Removing user's crontab:"; + system('/usr/bin/crontab', '-u', $login_name, '-r'); + print STDERR " done.\n"; +} + +# +# Remove the user's at jobs, if any +# (probably also needs to be done before password databases are updated) + +&remove_at_jobs($login_name, $uid); + +# +# Copy master password file to new file less removed user's entry + +&update_passwd_file; + +# +# Remove the user from all groups in /etc/group + +&update_group_file($login_name); + +# +# Remove the user's home directory + +if ($remove_directory) { + print STDERR "Removing user's home directory ($home_dir):"; + &remove_dir($home_dir); + print STDERR " done.\n"; +} + +# +# Remove the user's incoming mail file + +if (-e "$mail_dir/$login_name" || -l "$mail_dir/$login_name") { + print STDERR "Removing user's incoming mail file ($mail_dir/$login_name):"; + unlink "$mail_dir/$login_name" || + print STDERR "\n${whoami}: warning: unlink on $mail_dir/$login_name failed ($!) - continuing\n"; + print STDERR " done.\n"; +} + +# +# All done! + +exit 0; + +sub get_login_name { + # + # Get new user's name + local($done, $login_name); + + for ($done = 0; ! $done; ) { + print "Enter login name for user to remove: "; + $login_name = <>; + chop $login_name; + if (!($login_name =~ /[A-Za-z0-9_]/)) { + print STDERR "Sorry, login name must contain alphanumeric characters only.\n"; + } elsif (length($login_name) > 8 || length($login_name) == 0) { + print STDERR "Sorry, login name must be eight characters or less.\n"; + } else { + $done = 1; + } + } + + print "User name is ${login_name}\n" if $debug; + return($login_name); +} + +sub check_login_name { + # + # Check to see whether login name is in password file + local($login_name) = @_; + local($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire, + $Mgecos, $Mhome_dir, $Mshell); + local($i); + + seek(MASTER_PW, 0, 0); + while ($i = <MASTER_PW>) { + chop $i; + ($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire, + $Mgecos, $Mhome_dir, $Mshell) = split(/:/, $i); + if ($Mname eq $login_name) { + seek(MASTER_PW, 0, 0); + return($i); # User is in password database + } + } + seek(MASTER_PW, 0, 0); + + return '0'; # User wasn't found +} + +sub get_yn { + # + # Get a yes or no answer; return 'Y' or 'N' + local($prompt) = @_; + local($done, $ans); + + for ($done = 0; ! $done; ) { + print $prompt; + $ans = <>; + chop $ans; + $ans =~ tr/a-z/A-Z/; + if (!($ans =~ /^[YN]/)) { + print STDERR "Please answer (y)es or (n)o.\n"; + } else { + $done = 1; + } + } + + return(substr($ans, 0, 1)); +} + +sub update_passwd_file { + local($skipped, $i); + + print STDERR "Updating password file,"; + seek(MASTER_PW, 0, 0); + open(NEW_PW, ">$new_passwd_file") || + die "\n${whoami}: Error: Couldn't open file ${new_passwd_file}:\n $!\n"; + chmod(0600, $new_passwd_file) || + print STDERR "\n${whoami}: warning: couldn't set mode of $new_passwd_file to 0600 ($!)\n\tcontinuing, but please check mode of /etc/master.passwd!\n"; + $skipped = 0; + while ($i = <MASTER_PW>) { + if ($i =~ /\n$/) { + chop $i; + } + if ($i ne $pw_ent) { + print NEW_PW "$i\n"; + } else { + print STDERR "Dropped entry for $login_name\n" if $debug; + $skipped = 1; + } + } + close(NEW_PW); + seek(MASTER_PW, 0, 0); + + if ($skipped == 0) { + print STDERR "\n${whoami}: Whoops! Didn't find ${login_name}'s entry second time around!\n"; + unlink($new_passwd_file) || + print STDERR "\n${whoami}: warning: couldn't unlink $new_passwd_file ($!)\n\tPlease investigate, as this file should not be left in the filesystem\n"; + &unlockpw; + exit 1; + } + + # + # Run pwd_mkdb to install the updated password files and databases + + print STDERR " updating databases,"; + system('/usr/sbin/pwd_mkdb', '-p', ${new_passwd_file}); + print STDERR " done.\n"; + + close(MASTER_PW); # Not useful anymore +} + +sub update_group_file { + local($login_name) = @_; + + local($i, $j, $grmember_list, $new_grent); + local($grname, $grpass, $grgid, $grmember_list, @grmembers); + + print STDERR "Updating group file:"; + open(GROUP, $group_file) || + die "\n${whoami}: Error: couldn't open ${group_file}: $!\n"; + if (!flock(GROUP, &LOCK_EX|&LOCK_NB)) { + print STDERR "\n${whoami}: Error: couldn't lock ${group_file}: $!\n"; + exit 1; + } + local($group_perms, $group_uid, $group_gid) = + (stat(GROUP))[2, 4, 5]; # File Mode, uid, gid + open(NEW_GROUP, ">$new_group_file") || + die "\n${whoami}: Error: couldn't open ${new_group_file}: $!\n"; + chmod($group_perms, $new_group_file) || + printf STDERR "\n${whoami}: warning: could not set permissions of new group file to %o ($!)\n\tContinuing, but please check permissions of $group_file!\n", $group_perms; + chown($group_uid, $group_gid, $new_group_file) || + print STDERR "\n${whoami}: warning: could not set owner/group of new group file to ${group_uid}/${group_gid} ($!)\n\rContinuing, but please check ownership of $group_file!\n"; + while ($i = <GROUP>) { + if (!($i =~ /$login_name/)) { + # Line doesn't contain any references to the user, so just add it + # to the new file + print NEW_GROUP $i; + } else { + # + # Remove the user from the group + if ($i =~ /\n$/) { + chop $i; + } + ($grname, $grpass, $grgid, $grmember_list) = split(/:/, $i); + @grmembers = split(/,/, $grmember_list); + undef @new_grmembers; + local(@new_grmembers); + foreach $j (@grmembers) { + if ($j ne $login_name) { + push(new_grmembers, $j); + } elsif ($debug) { + print STDERR "Removing $login_name from group $grname\n"; + } + } + if ($grname eq $login_name && $#new_grmembers == -1) { + # Remove a user's personal group if empty + print STDERR "Removing group $grname -- personal group is empty\n"; + } else { + $grmember_list = join(',', @new_grmembers); + $new_grent = join(':', $grname, $grpass, $grgid, $grmember_list); + print NEW_GROUP "$new_grent\n"; + } + } + } + close(NEW_GROUP); + rename($new_group_file, $group_file) || # Replace old group file with new + die "\n${whoami}: error: couldn't rename $new_group_file to $group_file ($!)\n"; + close(GROUP); # File handle is worthless now + print STDERR " done.\n"; +} + +sub remove_dir { + # Recursively remove directories + local($dir) = @_; + + if (!(-d $dir)) { + print STDERR "${whoami}: Error: $dir is not a directory\n"; + return; + } + system('/bin/rm', '-rf', $dir); +} + +sub remove_at_jobs { + local($login_name, $uid) = @_; + local($i, $owner, $found); + + $found = 0; + opendir(ATDIR, $atjob_dir) || return; + while ($i = readdir(ATDIR)) { + next if $i eq '.'; + next if $i eq '..'; + next if $i eq '.lockfile'; + + $owner = (stat("$atjob_dir/$i"))[4]; # UID + if ($uid == $owner) { + if (!$found) { + print STDERR "Removing user's at jobs:"; + $found = 1; + } + # Use atrm to remove the job + print STDERR " $i"; + system('/usr/bin/atrm', $i); + } + } + closedir(ATDIR); + if ($found) { + print STDERR " done.\n"; + } +} |