Skip site navigation (1)Skip section navigation (2)
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>