Date: Sat, 16 Nov 2002 15:40:44 +0100 (CET) From: Eirik Nygaard <eirikn@bluezone.no> To: FreeBSD-gnats-submit@FreeBSD.org Subject: bin/45337: Rewritten rmuser in C Message-ID: <200211161440.gAGEei2p017251@odin.eirikn.net>
next in thread | raw e-mail | index | archive | help
>Number: 45337
>Category: bin
>Synopsis: Rewritten rmuser in C
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-bugs
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: update
>Submitter-Id: current-users
>Arrival-Date: Sat Nov 16 06:50:01 PST 2002
>Closed-Date:
>Last-Modified:
>Originator: Eirik Nygaard
>Release: FreeBSD 5.0-CURRENT i386
>Organization:
HFUG
>Environment:
System: FreeBSD odin.eirikn.net 5.0-CURRENT FreeBSD 5.0-CURRENT #5: Thu Nov 14 17:32:24 CET 2002 eirik@odin.eirikn.net:/usr/obj/usr/src/sys/ITvision i386
>Description:
A replacement for the rmuser.perl script rewritten in C so -CURRENT users
don't need to install perl to use the rmuser command. Missing a remove at
jobs since I got noe experience with at.
>How-To-Repeat:
Compile and run rmuser.c
>Fix:
Attached the rewritten rmuser program.
--- rmuser.c begins here ---
/*
* Copyright 2002 Eirik Nygaard. 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 EIRIK NYGAARD ``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.
*
* rmuser - C programme to remove users
*
* Eirik Nygaard <eirikn@bluezone.no>, 08/08/02
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <err.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
char passwd_file[] = "/etc/master.passwd";
char passwd_tmp[PATH_MAX] = "/etc/ptmp.XXXXX";
char group_file[] = "/etc/group";
char new_group_file[] = "/etc/group.new";
char mail_dir[] = "/var/mail";
char crontab_dir[] = "/var/cron/tabs";
char path[] = "/bin:/sbin:/usr/bin:/usr/sbin";
int removehomedir = 1;
char *user = NULL; /* User to delete */
char user2[BUFSIZ];
int fp;
FILE *fp2;
char line[PATH_MAX + 50];
char homedir[PATH_MAX];
void usage(void);
void getuser(void);
void remove_files_from_dir(int uid, char *path);
int recvnl(char *buf, int fd);
void update_passwd(void);
void update_group(void);
void killuser(int uid, int gid);
void del_mail(void);
void sighandle(int sig);
int
main(int argc, char **argv)
{
char answer[BUFSIZ];
int ch, numbuf = 0;
int yes = 0;
struct passwd *password;
struct stat sb;
/* Check for root */
if (getuid() != 0)
errx(EX_NOPERM, "You must be root to run this program.");
signal(SIGINT, sighandle);
signal(SIGTERM, sighandle);
signal(SIGHUP, sighandle);
signal(SIGQUIT, sighandle);
/* Set the path we need */
setenv("PATH", path, 1);
/* Set umode */
umask(022);
/* Get command line arguments */
while ((ch = getopt(argc, argv, "yu:")) != -1)
{
switch (ch)
{
case 'y':
yes = 1;
break;
case 'u':
user = optarg;
break;
case '?':
default:
usage();
}
}
if (user == NULL)
{
getuser();
}
if ((password = getpwnam(user)) == NULL)
errx(EX_NOUSER, "No user found by that name: %s.\n", user);
printf("\nMatching password entry: \n");
printf("\t%s:%s:%d:%d:%s:%s\n", password->pw_name, password->pw_passwd, password->pw_uid, password->pw_gid, password->pw_dir, password->pw_shell);
if (yes == 0)
{
printf("Is this the entry you wish to remove?(y/n) ");
fgets(answer, sizeof(answer), stdin);
if (strncmp(answer, "y", 1) != 0 && strncmp(answer, "Y", 1) != 0)
{
printf("User %s not removed.\n", user);
exit(EX_NOUSER);
}
}
if ((password = getpwnam(user)) == NULL)
errx(EX_NOUSER, "No user found by that name: %s.\n", user);
if (yes == 0)
{
printf("Remove homedir(%s)?(y/n) ", password->pw_dir);
fgets(answer, sizeof(answer), stdin);
if (strncmp(answer, "y", 1) == 0 || strncmp(answer, "Y", 1) == 0)
removehomedir = 1;
else
removehomedir = 0;
} else
removehomedir = 1;
strlcpy(homedir, password->pw_dir, sizeof(homedir));
next:
lstat(homedir, &sb);
if (removehomedir == 1)
{
if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode))
{
warnx("Home %s is not a directory, so it won't be removed\n", homedir);
removehomedir = 0;
}
if (S_ISLNK(sb.st_mode))
{
numbuf = readlink(password->pw_dir, homedir, sizeof(homedir));
printf("%s\n", homedir);
homedir[numbuf] = '\0';
goto next;
}
if (sb.st_uid != password->pw_uid)
{
if (removehomedir == 1)
warnx("Home %s is not owned by %s, so it will not be removed.\n", homedir, user);
removehomedir = 0;
}
}
killuser(password->pw_uid, password->pw_gid);
update_passwd();
update_group();
if (removehomedir == 1)
{
printf("Removeing home %s.\n", homedir);
remove_files_from_dir(password->pw_uid, homedir);
if (rmdir(homedir) == -1)
warn("Warning: Unable to remove home %s - continuing.\n", homedir);
}
snprintf(line, sizeof(line), "%s/%s", crontab_dir, user);
if ((fp2 = fopen(line, "r")) != NULL)
{
fclose(fp2);
if (unlink(line) == -1)
warn("Warning: Unable to remove crontab file %s - continuing.\n", line);
printf("Removeing users' crontab: ");
snprintf(line, sizeof(line), "/usr/bin/crontab -u %s -r", user);
system(line);
printf("done.\n");
}
del_mail();
printf("Removing files belonging to %s from /tmp.\n", user);
remove_files_from_dir(password->pw_uid, "/tmp");
printf("Removing files belonging to %s from /var/tmp.\n", user);
remove_files_from_dir(password->pw_uid, "/var/tmp");
return 0;
}
void
usage(void)
{
printf("Usage: %s [-y] [-u username]\n", getprogname());
exit(EX_USAGE);
}
void
getuser(void)
{
printf("Enter login name for user to remove: ");
fgets(user2, sizeof(user2), stdin);
user2[strlen(user2) - 1] = '\0';
user = user2;
}
/* Remove all files and folders belonging to uid in path */
void
remove_files_from_dir(int uid, char *path)
{
int Tmp;
struct dirent *DirEntryPtr;
DIR *DirPtr;
struct stat Stat;
char Path[PATH_MAX];
DirPtr = opendir(path);
while (1)
{
DirEntryPtr = readdir(DirPtr);
if (DirEntryPtr == 0)
break;
if (strcmp(DirEntryPtr->d_name, ".") != 0 && strcmp(DirEntryPtr->d_name, "..") != 0)
{
Path[0] = 0;
strlcat(Path, homedir, sizeof(Path));
strlcat(Path, "/", sizeof(Path));
strlcat(Path, DirEntryPtr->d_name, sizeof(Path));
Tmp = lstat(Path, &Stat);
if (S_ISDIR(Stat.st_mode))
{
remove_files_from_dir(uid, Path);
if (Stat.st_uid == uid)
if (rmdir(Path) == -1)
warn("Warning: Unable to remove dir %s - continuing.\n", Path);
}
if (Stat.st_uid == uid)
{
/* printf("Removeing file %s\n", Path); */
if (unlink(Path) == -1)
warn("Warning: unlink on %s failed - continuing.\n", Path);
}
}
}
closedir(DirPtr);
}
/* Remove the user from the passwd file */
void
update_passwd(void)
{
int fp_pw, fp2_pw;
char string[BUFSIZ], string2[BUFSIZ];
int skipped = 0;
if ((fp_pw = open(passwd_file, O_RDONLY | O_EXLOCK)) == -1)
{
warn("Warning: Unable to open %s, so can not remove the user - continuing.\n", passwd_file);
return;
}
if ((fp2_pw = open(passwd_tmp, O_WRONLY | O_EXLOCK | O_CREAT, 0600)) == -1)
{
printf("Warning: Unable to open %s, so can not remove the user - continuing.\n", passwd_tmp);
return;
}
snprintf(string2, sizeof(string2), "%s:", user);
while (recvnl(string, fp) != -1)
{
if (strncmp(string, "#", 1) == 0)
{
write(fp2_pw, string, strlen(string));
continue;
}
if (strncasecmp(string, string2, strlen(user) + 1) != 0)
{
write(fp2_pw, string, strlen(string));
write(fp2_pw, "\n", 1);
} else
{
if (skipped == 1)
{
write(fp2_pw, string, strlen(string));
write(fp2_pw, "\n", 1);
}
/* printf("Droped entry for %s\n", string); */
skipped = 1;
}
}
if (skipped == 0)
{
printf("Whoops! Didn't find %s's entry second time around!\n", user);
close(fp_pw);
close(fp2_pw);
exit(EX_NOUSER);
}
close(fp_pw);
close(fp2_pw);
/* Rebuild db */
snprintf(line, sizeof(line), "/usr/sbin/pwd_mkdb -p %s", passwd_tmp);
system(line);
}
/* Remove user from the group file and the users group */
void
update_group()
{
char string[BUFSIZ], string2[BUFSIZ], string3[BUFSIZ];
int fp_g, fp2_g, users = 0;
char *p, *p2;
int a = 0;
char group[BUFSIZ], gid[BUFSIZ], pass[BUFSIZ], users2[BUFSIZ],
users3[BUFSIZ];
if ((fp_g = open(group_file, O_RDONLY | O_EXLOCK)) == -1)
{
printf("Warning: Unable to open %s, so can not remove the user's group - continuing.\n", passwd_file);
return;
}
if ((fp2_g = open(new_group_file, O_WRONLY | O_EXLOCK | O_CREAT, 0644)) == -1)
{
printf("Warning: Unable to open %s, so can not remove the user's group - continuing.\n", passwd_tmp);
return;
}
snprintf(string2, sizeof(string2), "%s:", user);
while (recvnl(string, fp_g) != -1)
{
if (strncmp(string, "#", 1) == 0)
{
write(fp2_g, string, strlen(string));
continue;
}
if (strncasecmp(string, string2, strlen(user) + 1) != 0)
{
/* Check if user is in the group */
string3[0] = '\0';
strlcpy(string3, string, sizeof(string3));
for (p = strtok(string3, ":\n"); p != NULL; p = strtok(NULL, ":\n"))
{
switch (a)
{
case 0:
strlcpy(group, p, sizeof(group));
break;
case 1:
strlcpy(pass, p, sizeof(pass));
break;
case 2:
strlcpy(gid, p, sizeof(gid));
break;
case 3:
strlcpy(users2, p, sizeof(users2));
break;
}
a++;
}
a = 0;
users3[0] = '\0';
for (p = strtok(users2, ",\n"); p != NULL; p = strtok(NULL, ",\n"))
{
if (strcasecmp(user, p) != 0)
{
if (a == 0)
{
strlcat(users3, p, sizeof(users3));
a = 1;
} else
{
strlcat(users3, ",", sizeof(users3));
strlcat(users3, p, sizeof(users3));
}
}
}
snprintf(string3, sizeof(string3), "%s:%s:%s:%s\n", group, pass, gid, users3);
write(fp2_g, string3, strlen(string3));
a = 0;
} else
{
/* Check if there is other users added to the group */
p = string;
while (*p != ':')
*p++; /* Skip groupname */
*p++;
while (*p != ':')
*p++; /* Skip password section */
*p++;
while (*p != ':')
*p++; /* Skip gid section */
*p++;
strlcpy(string3, p, sizeof(string3));
for (p2 = strtok(string3, ","); p2 != NULL; p2 = strtok(NULL, ","))
{
users = 1;
}
if (users == 1)
{
warnx("Warning: Other users in group %s, not removing - continuing.\n", user);
write(fp2_g, string, strlen(string));
}
}
}
close(fp_g);
close(fp2_g);
rename(new_group_file, group_file);
}
/* Recieve a string from fd untill a newline */
int
recvnl(char *buf, int fd)
{
int bytes = 0;
char buf2[2];
char buf3[BUFSIZ];
buf3[0] = '\0';
while (read(fd, buf2, 1) != 0)
{
buf2[1] = '\0';
if (strncmp(buf2, "\n", 1) == 0)
{
strlcpy(buf, buf3, BUFSIZ);
return bytes;
} else
{
strlcat(buf3, buf2, sizeof(buf3));
bytes++;
}
}
return -1;
}
/* Kill users processes */
void
killuser(int uid, int gid)
{
int pid;
if ((pid = fork()) == 0)
{
if (setgid(gid) == -1)
{
warn("Warning: Unable to set gid - continuing.\n");
return;
}
if (setuid(uid) == -1)
{
warn("Warning: Unable to set uid - continuing.\n");
return;
}
kill(0, SIGTERM);
sleep(1);
kill(0, SIGKILL);
exit(EX_OK);
}
if (pid == -1)
{
warn("Error creating child processes - continuing.\n");
return;
} else
{
while (waitpid(-1, NULL, 0) != pid)
; /* Do nothing */
}
}
void
del_mail(void)
{
char string[BUFSIZ];
snprintf(string, sizeof(string), "%s/%s", mail_dir, user);
if (unlink(string) < 0 && errno != ENOENT)
warn("can't remove mailbox: %s", string);
}
/* Signal handeling */
void
sighandle(int sig)
{
char unknown[] = "UNKNOWN", sint[] = "SIGINT", squit[] = "SIGQUIT";
char shup[] = "SIGHUP", sterm[] = "SIGTERM";
struct iovec vec[3];
char msg1[] = "\nCaught signal ", msg2[] = " -- cleaning up\n";
vec[0].iov_base = msg1;
vec[0].iov_len = sizeof(msg1) - 1;
vec[2].iov_base = msg2;
vec[2].iov_len = sizeof(msg2) - 1;
switch (sig)
{
case SIGINT:
vec[1].iov_base = sint;
vec[1].iov_len = sizeof(sint) - 1;
break;
case SIGQUIT:
vec[1].iov_base = squit;
vec[1].iov_len = sizeof(squit) - 1;
break;
case SIGTERM:
vec[1].iov_base = sterm;
vec[1].iov_len = sizeof(sterm) - 1;
break;
case SIGHUP:
vec[1].iov_base = shup;
vec[1].iov_len = sizeof(shup) - 1;
break;
default:
vec[1].iov_base = unknown;
vec[1].iov_len = sizeof(unknown) - 1;
break;
}
writev(STDOUT_FILENO, vec, sizeof(vec) / sizeof(*vec));
unlink(passwd_tmp);
unlink(new_group_file);
close(fp);
exit(EX_OK);
}
--- rmuser.c ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200211161440.gAGEei2p017251>
