Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 9 Nov 2002 16:27:12 +0100
From:      Eirik Nygaard <eirikn@bluezone.no>
To:        current@freebsd.org
Subject:   rmuser
Message-ID:  <20021109152711.GA746@eirikn.net>

next in thread | raw e-mail | index | archive | help

--U+BazGySraz5kW0T
Content-Type: multipart/mixed; boundary="/9DWx/yDrRhgMJTb"
Content-Disposition: inline


--/9DWx/yDrRhgMJTb
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable

I have rewritten the rmuser.perl script into C. But got no experiense with =
at, and I see the the perl port got a function that removes any at jobs for=
 the user being removed. So I wonderd if anyone could make a patch that doe=
s that, any feedback on the code or bug reports would also be greate.=20

I have sent in a mail once before, fixed all the style bugs since them :)
The source is attached.

--=20

Eirik Nygaard <eirikn@bluezone.no>
PGP Key: 83C55EDE


--/9DWx/yDrRhgMJTb
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="rmuser.c"
Content-Transfer-Encoding: quoted-printable

/*
 * Copyright 2002 Eirik Nygaard.=20
 * 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[] =3D "/etc/master.passwd";
char	passwd_tmp[PATH_MAX] =3D "/etc/ptmp.XXXXX";
char	*passwd_tmp2;
char	group_file[] =3D "/etc/group";
char	new_group_file[] =3D "/etc/group.new";
char	mail_dir[] =3D "/var/mail";
char	crontab_dir[] =3D "/var/cron/tabs";
char	path[] =3D "/bin:/sbin:/usr/bin:/usr/sbin";

int	yes;			/* Always yes? */
int	removehomedir =3D 1;
char	*user =3D NULL;			/* User to delete */
char	user2[BUFSIZ];
char	answer[BUFSIZ];
int	fp;
FILE	*fp2;
char	line[PATH_MAX + 50];
char	homedir[PATH_MAX];

struct passwd	*password;
struct stat		sb;

void	usage();
void	getuser();
void	remove_files_from_dir(int uid, char *path);
int	recvnl( char *buf, int fd);
void	update_passwd();
void	update_group();
void	killuser(int uid, int gid);
void	del_mail();
void	sighandle(int sig);

int=20
main(int argc, char **argv)=20
{
	int	ch, numbuf =3D 0;

	/* Check for root */
	if (getuid() !=3D 0)=20
		errx(EX_NOPERM, "You must be root to run this program.");
=09

	signal(SIGINT, sighandle);
	signal(SIGTERM, sighandle);
	signal(SIGHUP, sighandle);
	signal(SIGQUIT, sighandle);
=09
	/* Set the path we need */
	setenv("PATH", path, 1);
=09
	/* Set umode */
	umask(022);
=09
	/* Get command line arguments */
	while ((ch =3D getopt(argc, argv, "yu:")) !=3D -1) {
		switch (ch) {
			case 'y':
				yes =3D 1;
				break;
			=09
			case 'u':
				user =3D optarg;
				break;
			=09
			case '?':
			default:
				usage();
		}
	}
=09
	if (user =3D=3D NULL) {
		getuser();
	}
=09
	if ((password =3D getpwnam(user)) =3D=3D NULL)=20
		errx(EX_NOUSER, "No user found by that name: %s.\n", user);
=09
	printf("\nMatching password entry: \n");
	printf("\t%s:%s:%d:%d:%s:%s\n", password->pw_name, password->pw_passwd, pa=
ssword->pw_uid, password->pw_gid, password->pw_dir, password->pw_shell);
=09
	if (yes =3D=3D 0) {
		printf("Is this the entry you wish to remove?(y/n) ");
		fgets(answer, sizeof(answer), stdin);
		if (strncmp(answer, "y", 1) !=3D 0 && strncmp(answer, "Y", 1) !=3D 0) {
			printf("User %s not removed.\n", user);
			exit(1);
		}
	}
=09
	if ((password =3D getpwnam(user)) =3D=3D NULL)=20
		errx(EX_NOUSER, "No user found by that name: %s.\n", user);
=09
=09
	if (yes =3D=3D 0) {
		printf("Remove homedir(%s)?(y/n) ", password->pw_dir);
		fgets(answer, sizeof(answer), stdin);
		if (strncmp(answer, "y", 1) =3D=3D 0 || strncmp(answer, "Y", 1) =3D=3D 0)=
=20
			removehomedir =3D 1;
		else=20
			removehomedir =3D 0;
	} else=20
		removehomedir =3D 1;
=09
=09
	strncpy(homedir, password->pw_dir, sizeof(homedir));
	next:
	lstat(homedir, &sb);
	if (removehomedir =3D=3D 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 =3D 0;
		}
	=09
		if (S_ISLNK(sb.st_mode)) {
			numbuf =3D readlink(password->pw_dir, homedir, sizeof(homedir));
			printf("%s\n", homedir);
			homedir[numbuf] =3D '\0';
			goto next;
		}
	=09
		if (sb.st_uid !=3D password->pw_uid) {
			if (removehomedir =3D=3D 1)
				warnx("Home %s is not owned by %s, so it will not be removed.\n", homed=
ir, user);
			removehomedir =3D 0;
		}
	=09
	}
=09
	killuser(password->pw_uid, password->pw_gid);
	update_passwd();
	update_group();
	if (removehomedir =3D=3D 1) {
		printf("Removeing home %s.\n", homedir);
		remove_files_from_dir(password->pw_uid, homedir);
		if (rmdir(homedir) =3D=3D -1)
			warn("Warning: Unable to remove home %s - continuing.\n", homedir);
	}
=09
	snprintf(line, sizeof(line), "%s/%s", crontab_dir, user);
	if ((fp2 =3D fopen(line, "r")) !=3D NULL) {
		fclose(fp2);
		if (unlink(line) =3D=3D -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");
	}
=09
	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");
=09
	return 0;
}

void=20
usage()
{

	printf("Usage: %s [-y] [-u username]\n", getprogname());=09
	exit(1);
}

void=20
getuser()
{

	printf("Enter login name for user to remove: ");
	fgets(user2, sizeof(user2), stdin);
	user2[strlen(user2) - 1] =3D '\0';
	user =3D user2;
}

/* Remove all files and folders belonging to uid in path */
void=20
remove_files_from_dir(int uid, char *path)
{
	int	Tmp;
	struct dirent	*DirEntryPtr;
	DIR	*DirPtr;
	struct stat		Stat;
	char	Path[PATH_MAX];
=09
	DirPtr =3D opendir(path);
	while (1) {
		DirEntryPtr =3D readdir(DirPtr);=09
		if (DirEntryPtr =3D=3D 0) break;
		if (strcmp(DirEntryPtr->d_name,".") !=3D 0 && strcmp(DirEntryPtr->d_name,=
"..") !=3D 0) {
			Path[0] =3D 0;
			strlcat(Path,homedir, sizeof(Path));
			strlcat(Path,"/", sizeof(Path));
			strlcat(Path,DirEntryPtr->d_name, sizeof(Path));
			Tmp =3D lstat(Path,&Stat);=20
			if (S_ISDIR(Stat.st_mode)) {
				remove_files_from_dir(uid, Path);
				if (Stat.st_uid =3D=3D uid)
					if (rmdir(Path) =3D=3D -1)
				 		warn("Warning: Unable to remove dir %s - continuing.\n", Path);
			}
			if (Stat.st_uid =3D=3D uid) {
				/* printf("Removeing file %s\n", Path); */
				if (unlink(Path) =3D=3D -1)=20
					warn("Warning: unlink on %s failed - continuing.\n", Path);
			}
		}
	=09
	}

	closedir(DirPtr);

}

/* Remove the user from the passwd file */
void=20
update_passwd()
{
	int	fp_pw, fp2_pw;
	char	string[BUFSIZ], string2[BUFSIZ];
	int	skipped =3D 0;
=09
	if ((fp_pw =3D open(passwd_file, O_RDONLY|O_EXLOCK)) =3D=3D -1) {
		warn("Warning: Unable to open %s, so can not remove the user - continuing=
.\n", passwd_file);
		return;
	}
=09
	if ((fp2_pw =3D open(passwd_tmp, O_WRONLY|O_EXLOCK|O_CREAT, 0600)) =3D=3D =
-1) {
		printf("Warning: Unable to open %s, so can not remove the user - continui=
ng.\n", passwd_tmp);
		return;
	}
=09
	snprintf(string2, sizeof(string2), "%s:", user);
	while (recvnl(string, fp) !=3D -1) {
		if (strncmp(string, "#", 1) =3D=3D 0)  {
			write(fp2_pw, string, strlen(string));
			continue;
		}
		if (strncasecmp(string, string2, strlen(user) + 1) !=3D 0) {
			write(fp2_pw, string, strlen(string));
			write(fp2_pw, "\n", 1);
		} else {
			if (skipped =3D=3D 1) {
				write(fp2_pw, string, strlen(string));
				write(fp2_pw, "\n", 1);
			}
		=09
			/* printf("Droped entry for %s\n", string); */
			skipped =3D 1;
		}
	}
=09
	if (skipped =3D=3D 0) {
		printf("Whoops! Didn't find %s's entry second time around!\n", user);
		close(fp_pw); close(fp2_pw);
		exit(1);
	}
=09
	close(fp_pw); close(fp2_pw);
	/* Rebuild db */
	snprintf(line, sizeof(line), "/usr/sbin/pwd_mkdb -p %s", passwd_tmp);
	system(line);
=09
}

/* Remove user from the group file and the users group */
void=20
update_group()
{
	char	string[BUFSIZ], string2[BUFSIZ], string3[BUFSIZ];
	int	fp_g, fp2_g, users;
	char	*p, *p2;
	int	a =3D 0;
	char	group[BUFSIZ], gid[BUFSIZ], pass[BUFSIZ], users2[BUFSIZ], users3[BUFS=
IZ];

	if ((fp_g =3D open(group_file, O_RDONLY|O_EXLOCK)) =3D=3D -1) {
		printf("Warning: Unable to open %s, so can not remove the user's group - =
continuing.\n", passwd_file);
		return;
	}
=09
	if ((fp2_g =3D open(new_group_file, O_WRONLY|O_EXLOCK|O_CREAT, 0644)) =3D=
=3D -1) {
		printf("Warning: Unable to open %s, so can not remove the user's group - =
continuing.\n", passwd_tmp);
		return;
	}
=09
	snprintf(string2, sizeof(string2), "%s:", user);
	while (recvnl(string, fp_g) !=3D -1) {
		if (strncmp(string, "#", 1) =3D=3D 0) {
			write(fp2_g, string, strlen(string));
			continue;
		}

		if (strncasecmp(string, string2, strlen(user) + 1) !=3D 0) {
			/* Check if user is in the group */
			string3[0] =3D '\0';
			strlcpy(string3, string, sizeof(string3));
			for (p =3D strtok(string3, ":\n"); p !=3D NULL; p =3D strtok(NULL, ":\n"=
)) {
				switch(a) {
					case 0:
						strlcpy(group, p, sizeof(group));
						break;
					=09
					case 1:
						strlcpy(pass, p, sizeof(pass));
						break;
					=09
					case 2:
						strlcpy(gid, p, sizeof(gid));
						break;
					=09
					case 3:
						strlcpy(users2, p, sizeof(users2));
						break;
				}
				a++;
		=09
			}
		=09
			a =3D 0;
			users3[0] =3D '\0';
			for (p =3D strtok(users2, ",\n"); p !=3D NULL; p =3D strtok(NULL, ",\n")=
) {
				if (strcasecmp(user, p) !=3D 0) {
					if (a =3D=3D 0) {
						strlcat(users3, p, sizeof(users3));
						a =3D 1;
					} else {=20
						strlcat(users3, ",", sizeof(users3));
						strlcat(users3, p, sizeof(users3));
					}
				}
			}
		=09
			snprintf(string3, sizeof(string3), "%s:%s:%s:%s\n", group, pass, gid, us=
ers3);
		=09
			write(fp2_g, string3, strlen(string3));
			a =3D 0;

		} else {
			/* Check if there is other users added to the group */
			p =3D string;
			while (*p !=3D ':') *p++; /* Skip groupname */
			*p++;
			while (*p !=3D ':') *p++; /* Skip password section */
			*p++;
			while (*p !=3D ':') *p++; /* Skip gid section */
			*p++;
			strlcpy(string3, p, sizeof(string3));
			for (p2 =3D strtok(string3, ","); p2 !=3D NULL; p2 =3D strtok(NULL, ",")=
) {
				users =3D 1;
			}
		=09
			if (users =3D=3D 1) {
				warnx("Warning: Other users in group %s, not removing - continuing.\n",=
 user);
				write(fp2_g, string, strlen(string));
			} else {
				/* printf("Droped entry %s\n", string); */
			}
		}
	}
=09
	close(fp_g); close(fp2_g);
	rename(new_group_file, group_file);

}

/* Recieve a string from fd untill a \n */
int=20
recvnl(char *buf, int fd)
{
   int	bytes =3D 0;
	char	buf2[2];
	char	buf3[BUFSIZ];

	buf3[0] =3D '\0';
	while ( read(fd, buf2, 1) !=3D 0) {
		buf2[1] =3D '\0';
		if (strncmp(buf2, "\n", 1) =3D=3D 0) {
			strncpy(buf, buf3, BUFSIZ);
			return bytes;
		} else {
			strlcat(buf3, buf2, sizeof(buf3));
			bytes++;
		}
	}
=09
	return -1;
}

/* Kill users processes */
void=20
killuser(int uid, int gid)
{
	int	pid;=09
=09
	if (( pid =3D fork()) =3D=3D 0) {
		if (setgid(gid) =3D=3D -1) {
			warn("Warning: Unable to set gid - continuing.\n");
			return;
		}
		if (setuid(uid) =3D=3D -1) {
			warn("Warning: Unable to set uid - continuing.\n");
			return;
		}
	=09
		kill(0, SIGTERM);
		sleep(1);
		kill(0, SIGKILL);
		exit(1);
	}
=09
	if (pid =3D=3D -1) {
		warn("Error creating child processes - continuing.\n");
		return;
	} else {
		while (waitpid(-1,NULL,0) !=3D pid)
			;	/* Do nothing */
	}

}

void=20
del_mail()
{
	char	string[BUFSIZ];
=09
	snprintf(string, sizeof(string), "%s/%s", mail_dir, user);
	if (unlink(string) < 0 && errno !=3D ENOENT)
		warn("can't remove mailbox: %s", string);
=09
}

/* Signal handeling */
void=20
sighandle(int sig)
{
	char	unknown[] =3D "UNKNOWN", sint[] =3D "SIGINT", squit[] =3D "SIGQUIT";
	char	shup[] =3D "SIGHUP", sterm[] =3D "SIGTERM";
	struct iovec	vec[3];
	char	msg1[] =3D "\nCaught signal ", msg2[] =3D " -- cleaning up\n";
=09
	vec[0].iov_base =3D msg1; vec[0].iov_len =3D sizeof(msg1) - 1;
	vec[2].iov_base =3D msg2; vec[2].iov_len =3D sizeof(msg2) - 1;

	switch (sig) {
		case SIGINT:
			vec[1].iov_base =3D sint; vec[1].iov_len =3D sizeof(sint) - 1;
			break;

		case SIGQUIT:
			vec[1].iov_base =3D squit; vec[1].iov_len =3D sizeof(squit) - 1;
			break;

		case SIGTERM:
			vec[1].iov_base =3D sterm; vec[1].iov_len =3D sizeof(sterm) - 1;
			break;

		case SIGHUP:
			vec[1].iov_base =3D shup; vec[1].iov_len =3D sizeof(shup) - 1;
			break;

		default:
			vec[1].iov_base =3D unknown; vec[1].iov_len =3D sizeof(unknown) - 1;
			break;
	}

=09
	writev(STDOUT_FILENO, vec, sizeof(vec) / sizeof(*vec));
	unlink(passwd_tmp);
	unlink(new_group_file);
	close(fp);

	exit(1);
}


--/9DWx/yDrRhgMJTb--

--U+BazGySraz5kW0T
Content-Type: application/pgp-signature
Content-Disposition: inline

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (FreeBSD)

iD8DBQE9zSlP1JB0Z4PFXt4RAgDRAJ9C5XkSVZfCfcArsOzQzvn/MvcLPQCggD6t
DyASnHaHQ9bpcYmY1iSD+6M=
=gtCA
-----END PGP SIGNATURE-----

--U+BazGySraz5kW0T--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20021109152711.GA746>