From owner-freebsd-bugs Sat Nov 16 6:50:19 2002 Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 6381C37B404 for ; Sat, 16 Nov 2002 06:50:04 -0800 (PST) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 78EC443E77 for ; Sat, 16 Nov 2002 06:50:02 -0800 (PST) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.12.6/8.12.6) with ESMTP id gAGEo2x3041199 for ; Sat, 16 Nov 2002 06:50:02 -0800 (PST) (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.12.6/8.12.6/Submit) id gAGEo2Ru041196; Sat, 16 Nov 2002 06:50:02 -0800 (PST) Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 444B637B401 for ; Sat, 16 Nov 2002 06:40:55 -0800 (PST) Received: from odin.eirikn.net (a217-118-47-155.bluecom.no [217.118.47.155]) by mx1.FreeBSD.org (Postfix) with ESMTP id 09D3843E6E for ; Sat, 16 Nov 2002 06:40:53 -0800 (PST) (envelope-from eirik@odin.eirikn.net) Received: from odin.eirikn.net (localhost [127.0.0.1]) by odin.eirikn.net (8.12.6/8.12.6) with ESMTP id gAGEeihg017252 for ; Sat, 16 Nov 2002 15:40:45 +0100 (CET) (envelope-from eirik@odin.eirikn.net) Received: (from eirik@localhost) by odin.eirikn.net (8.12.6/8.12.6/Submit) id gAGEei2p017251; Sat, 16 Nov 2002 15:40:44 +0100 (CET) Message-Id: <200211161440.gAGEei2p017251@odin.eirikn.net> Date: Sat, 16 Nov 2002 15:40:44 +0100 (CET) From: Eirik Nygaard Reply-To: Eirik Nygaard To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Subject: bin/45337: Rewritten rmuser in C Sender: owner-freebsd-bugs@FreeBSD.ORG Precedence: bulk List-ID: List-Archive: (Web Archive) List-Help: (List Instructions) List-Subscribe: List-Unsubscribe: X-Loop: FreeBSD.org >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 , 08/08/02 * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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