2 * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3 * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4 * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5 * Copyright (c) 2007 - 2008, Nicolas François
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the copyright holders or contributors may not be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #ident "$Id: userdel.c 2979 2009-05-22 10:41:10Z nekral-guest $"
46 #ifdef ACCT_TOOLS_SETUID
50 #endif /* ACCT_TOOLS_SETUID */
55 #include "prototypes.h"
63 #include "exitcodes.h"
68 #define E_PW_UPDATE 1 /* can't update password file */
69 #define E_NOTFOUND 6 /* specified user doesn't exist */
70 #define E_USER_BUSY 8 /* user currently logged in */
71 #define E_GRP_UPDATE 10 /* can't update group file */
72 #define E_HOMEDIR 12 /* can't remove home directory */
79 static char *user_name;
81 static char *user_home;
83 static bool fflg = false;
84 static bool rflg = false;
86 static bool is_shadow_pwd;
89 static bool is_shadow_grp;
90 static bool sgr_locked = false;
92 static bool pw_locked = false;
93 static bool gr_locked = false;
94 static bool spw_locked = false;
96 /* local function prototypes */
97 static void usage (void);
98 static void update_groups (void);
99 static void close_files (void);
100 static void fail_exit (int);
101 static void open_files (void);
102 static void update_user (void);
103 static void user_cancel (const char *);
105 #ifdef EXTRA_CHECK_HOME_DIR
106 static bool path_prefix (const char *, const char *);
108 static int is_owner (uid_t, const char *);
109 static int remove_mailbox (void);
112 * usage - display usage message and exit
114 static void usage (void)
116 fputs (_("Usage: userdel [options] LOGIN\n"
119 " -f, --force force removal of files,\n"
120 " even if not owned by user\n"
121 " -h, --help display this help message and exit\n"
122 " -r, --remove remove home directory and mail spool\n"
128 * update_groups - delete user from secondary group set
130 * update_groups() takes the user name that was given and searches
131 * the group files for membership in any group.
133 * we also check to see if they have any groups they own (the same
134 * name is their user name) and delete them too (only if USERGROUPS_ENAB
137 static void update_groups (void)
139 const struct group *grp;
144 bool deleted_user_group = false;
145 const struct sgrp *sgrp;
147 #endif /* SHADOWGRP */
150 * Scan through the entire group file looking for the groups that
151 * the user is a member of.
153 for (gr_rewind (), grp = gr_next (); NULL != grp; grp = gr_next ()) {
156 * See if the user specified this group as one of their
159 if (!is_on_list (grp->gr_mem, user_name)) {
164 * Delete the username from the list of group members and
165 * update the group entry to reflect the change.
167 ngrp = __gr_dup (grp);
170 _("%s: Out of memory. Cannot update %s.\n"),
174 ngrp->gr_mem = del_list (ngrp->gr_mem, user_name);
175 if (gr_update (ngrp) == 0) {
177 _("%s: failed to prepare the new %s entry '%s'\n"),
178 Prog, gr_dbname (), ngrp->gr_name);
183 * Update the DBM group file with the new entry as well.
186 audit_logger (AUDIT_DEL_USER, Prog,
187 "deleting user from group",
188 user_name, (unsigned int) user_id,
189 SHADOW_AUDIT_SUCCESS);
191 SYSLOG ((LOG_INFO, "delete '%s' from group '%s'\n",
192 user_name, ngrp->gr_name));
196 * we've removed their name from all the groups above, so
197 * now if they have a group with the same name as their
198 * user name, with no members, we delete it.
199 * FIXME: below, the check for grp->gr_mem[0] is not sufficient.
200 * We should retrieve the group with gr_locate and check
201 * that gr_mem is empty.
203 grp = xgetgrnam (user_name);
205 && getdef_bool ("USERGROUPS_ENAB")
206 && ( (NULL == grp->gr_mem[0])
207 || ( (NULL == grp->gr_mem[1])
208 && (strcmp (grp->gr_mem[0], user_name) == 0)))) {
213 * Scan the passwd file to check if this group is still
214 * used as a primary group.
217 while ((pwd = getpwent ()) != NULL) {
218 if (strcmp (pwd->pw_name, user_name) == 0) {
221 if (pwd->pw_gid == grp->gr_gid) {
223 _("%s: group %s is the primary group of another user and is not removed.\n"),
233 * We can remove this group, it is not the primary
234 * group of any remaining user.
236 if (gr_remove (grp->gr_name) == 0) {
238 _("%s: cannot remove entry '%s' from %s\n"),
239 Prog, grp->gr_name, gr_dbname ());
240 fail_exit (E_GRP_UPDATE);
244 deleted_user_group = true;
248 audit_logger (AUDIT_DEL_GROUP, Prog,
250 grp->gr_name, AUDIT_NO_ID,
251 SHADOW_AUDIT_SUCCESS);
254 "removed group '%s' owned by '%s'\n",
255 grp->gr_name, user_name));
259 if (!is_shadow_grp) {
264 * Scan through the entire shadow group file looking for the groups
265 * that the user is a member of. Both the administrative list and
266 * the ordinary membership list is checked.
268 for (sgr_rewind (), sgrp = sgr_next ();
270 sgrp = sgr_next ()) {
271 bool was_member, was_admin;
274 * See if the user specified this group as one of their
277 was_member = is_on_list (sgrp->sg_mem, user_name);
278 was_admin = is_on_list (sgrp->sg_adm, user_name);
280 if (!was_member && !was_admin) {
284 nsgrp = __sgr_dup (sgrp);
287 _("%s: Out of memory. Cannot update %s.\n"),
288 Prog, sgr_dbname ());
293 nsgrp->sg_mem = del_list (nsgrp->sg_mem, user_name);
297 nsgrp->sg_adm = del_list (nsgrp->sg_adm, user_name);
300 if (sgr_update (nsgrp) == 0) {
302 _("%s: failed to prepare the new %s entry '%s'\n"),
303 Prog, sgr_dbname (), nsgrp->sg_name);
307 audit_logger (AUDIT_DEL_USER, Prog,
308 "deleting user from shadow group",
309 user_name, (unsigned int) user_id,
310 SHADOW_AUDIT_SUCCESS);
312 SYSLOG ((LOG_INFO, "delete '%s' from shadow group '%s'\n",
313 user_name, nsgrp->sg_name));
316 if ( deleted_user_group
317 && (sgr_locate (user_name) != NULL)) {
318 if (sgr_remove (user_name) == 0) {
320 _("%s: cannot remove entry '%s' from %s\n"),
321 Prog, user_name, sgr_dbname ());
322 fail_exit (E_GRP_UPDATE);
325 #endif /* SHADOWGRP */
329 * close_files - close all of the files that were opened
331 * close_files() closes all of the files that were opened for this
332 * new user. This causes any modified entries to be written out.
334 static void close_files (void)
336 if (pw_close () == 0) {
337 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ());
338 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
339 fail_exit (E_PW_UPDATE);
341 if (pw_unlock () == 0) {
342 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
343 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
349 if (spw_close () == 0) {
351 _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ());
352 SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
353 fail_exit (E_PW_UPDATE);
355 if (spw_unlock () == 0) {
356 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
357 SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
363 if (gr_close () == 0) {
364 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
365 SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
366 fail_exit (E_GRP_UPDATE);
368 if (gr_unlock () == 0) {
369 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
370 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
377 if (sgr_close () == 0) {
379 _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
380 SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
381 fail_exit (E_GRP_UPDATE);
384 if (sgr_unlock () == 0) {
385 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
386 SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
395 * fail_exit - exit with a failure code after unlocking the files
397 static void fail_exit (int code)
400 if (pw_unlock () == 0) {
401 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
402 SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
407 if (gr_unlock () == 0) {
408 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
409 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
414 if (spw_unlock () == 0) {
415 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
416 SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
422 if (sgr_unlock () == 0) {
423 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
424 SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
431 audit_logger (AUDIT_DEL_USER, Prog,
433 user_name, (unsigned int) user_id,
434 SHADOW_AUDIT_FAILURE);
441 * open_files - lock and open the password files
443 * open_files() opens the two password files.
446 static void open_files (void)
448 if (pw_lock () == 0) {
450 _("%s: cannot lock %s; try again later.\n"),
453 audit_logger (AUDIT_DEL_USER, Prog,
454 "locking password file",
455 user_name, (unsigned int) user_id,
456 SHADOW_AUDIT_FAILURE);
458 fail_exit (E_PW_UPDATE);
461 if (pw_open (O_RDWR) == 0) {
463 _("%s: cannot open %s\n"), Prog, pw_dbname ());
465 audit_logger (AUDIT_DEL_USER, Prog,
466 "opening password file",
467 user_name, (unsigned int) user_id,
468 SHADOW_AUDIT_FAILURE);
470 fail_exit (E_PW_UPDATE);
473 if (spw_lock () == 0) {
475 _("%s: cannot lock %s; try again later.\n"),
476 Prog, spw_dbname ());
478 audit_logger (AUDIT_DEL_USER, Prog,
479 "locking shadow password file",
480 user_name, (unsigned int) user_id,
481 SHADOW_AUDIT_FAILURE);
483 fail_exit (E_PW_UPDATE);
486 if (spw_open (O_RDWR) == 0) {
488 _("%s: cannot open %s\n"),
489 Prog, spw_dbname ());
491 audit_logger (AUDIT_DEL_USER, Prog,
492 "opening shadow password file",
493 user_name, (unsigned int) user_id,
494 SHADOW_AUDIT_FAILURE);
496 fail_exit (E_PW_UPDATE);
499 if (gr_lock () == 0) {
501 _("%s: cannot lock %s; try again later.\n"),
504 audit_logger (AUDIT_DEL_USER, Prog,
505 "locking group file",
506 user_name, (unsigned int) user_id,
507 SHADOW_AUDIT_FAILURE);
509 fail_exit (E_GRP_UPDATE);
512 if (gr_open (O_RDWR) == 0) {
513 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
515 audit_logger (AUDIT_DEL_USER, Prog,
516 "opening group file",
517 user_name, (unsigned int) user_id,
518 SHADOW_AUDIT_FAILURE);
520 fail_exit (E_GRP_UPDATE);
524 if (sgr_lock () == 0) {
526 _("%s: cannot lock %s; try again later.\n"),
527 Prog, sgr_dbname ());
529 audit_logger (AUDIT_DEL_USER, Prog,
530 "locking shadow group file",
531 user_name, (unsigned int) user_id,
532 SHADOW_AUDIT_FAILURE);
534 fail_exit (E_GRP_UPDATE);
537 if (sgr_open (O_RDWR) == 0) {
538 fprintf (stderr, _("%s: cannot open %s\n"),
539 Prog, sgr_dbname ());
541 audit_logger (AUDIT_DEL_USER, Prog,
542 "opening shadow group file",
543 user_name, (unsigned int) user_id,
544 SHADOW_AUDIT_FAILURE);
546 fail_exit (E_GRP_UPDATE);
553 * update_user - delete the user entries
555 * update_user() deletes the password file entries for this user
556 * and will update the group entries as required.
558 static void update_user (void)
560 if (pw_remove (user_name) == 0) {
562 _("%s: cannot remove entry '%s' from %s\n"),
563 Prog, user_name, pw_dbname ());
564 fail_exit (E_PW_UPDATE);
567 && (spw_locate (user_name) != NULL)
568 && (spw_remove (user_name) == 0)) {
570 _("%s: cannot remove entry '%s' from %s\n"),
571 Prog, user_name, spw_dbname ());
572 fail_exit (E_PW_UPDATE);
575 audit_logger (AUDIT_DEL_USER, Prog,
576 "deleting user entries",
577 user_name, (unsigned int) user_id,
578 SHADOW_AUDIT_SUCCESS);
580 SYSLOG ((LOG_INFO, "delete user '%s'\n", user_name));
584 * user_cancel - cancel cron and at jobs
586 * user_cancel calls a script for additional cleanups like removal of
587 * cron, at, or print jobs.
590 static void user_cancel (const char *user)
596 cmd = getdef_str ("USERDEL_CMD");
602 execl (cmd, cmd, user, (char *) 0);
604 exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
605 } else if ((pid_t)-1 == pid) {
610 wpid = wait (&status);
611 } while ((wpid != pid) && ((pid_t)-1 != wpid));
614 #ifdef EXTRA_CHECK_HOME_DIR
615 static bool path_prefix (const char *s1, const char *s2)
617 return ( (strncmp (s2, s1, strlen (s1)) == 0)
618 && ( ('\0' == s2[strlen (s1)])
619 || ('/' == s2[strlen (s1)])));
624 * is_owner - Check if path is owned by uid
627 * 1: path exists and is owned by uid
628 * 0: path is not owned by uid, or a failure occured
629 * -1: path does not exist
631 static int is_owner (uid_t uid, const char *path)
636 if (stat (path, &st) != 0) {
637 if ((ENOENT == errno) || (ENOTDIR == errno)) {
638 /* The file or directory does not exist */
644 return (st.st_uid == uid);
647 static int remove_mailbox (void)
654 maildir = getdef_str ("MAIL_DIR");
655 #ifdef MAIL_SPOOL_DIR
656 if ((NULL == maildir) && (getdef_str ("MAIL_FILE") == NULL)) {
657 maildir = MAIL_SPOOL_DIR;
660 if (NULL == maildir) {
663 snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name);
665 if (unlink (mailfile) != 0) {
667 _("%s: warning: can't remove %s: %s\n"),
668 Prog, mailfile, strerror (errno));
669 SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
671 audit_logger (AUDIT_DEL_USER, Prog,
672 "deleting mail file",
673 user_name, (unsigned int) user_id,
674 SHADOW_AUDIT_FAILURE);
682 audit_logger (AUDIT_DEL_USER, Prog,
683 "deleting mail file",
684 user_name, (unsigned int) user_id,
685 SHADOW_AUDIT_SUCCESS);
690 i = is_owner (user_id, mailfile);
693 _("%s: %s not owned by %s, not removing\n"),
694 Prog, mailfile, user_name);
696 "%s not owned by %s, not removed",
697 mailfile, strerror (errno)));
699 audit_logger (AUDIT_DEL_USER, Prog,
700 "deleting mail file",
701 user_name, (unsigned int) user_id,
702 SHADOW_AUDIT_FAILURE);
705 } else if (i == -1) {
706 return 0; /* mailbox doesn't exist */
708 if (unlink (mailfile) != 0) {
710 _("%s: warning: can't remove %s: %s\n"),
711 Prog, mailfile, strerror (errno));
712 SYSLOG ((LOG_ERR, "Cannot remove %s: %s", mailfile, strerror (errno)));
714 audit_logger (AUDIT_DEL_USER, Prog,
715 "deleting mail file",
716 user_name, (unsigned int) user_id,
717 SHADOW_AUDIT_FAILURE);
725 audit_logger (AUDIT_DEL_USER, Prog,
726 "deleting mail file",
727 user_name, (unsigned int) user_id,
728 SHADOW_AUDIT_SUCCESS);
735 * main - userdel command
737 int main (int argc, char **argv)
739 int errors = 0; /* Error in the removal of the home directory */
741 #ifdef ACCT_TOOLS_SETUID
743 pam_handle_t *pamh = NULL;
746 #endif /* ACCT_TOOLS_SETUID */
753 * Get my name so that I can use it to report errors.
755 Prog = Basename (argv[0]);
756 (void) setlocale (LC_ALL, "");
757 (void) bindtextdomain (PACKAGE, LOCALEDIR);
758 (void) textdomain (PACKAGE);
762 * Parse the command line options.
765 static struct option long_options[] = {
766 {"force", no_argument, NULL, 'f'},
767 {"help", no_argument, NULL, 'h'},
768 {"remove", no_argument, NULL, 'r'},
769 {NULL, 0, NULL, '\0'}
771 while ((c = getopt_long (argc, argv, "fhr",
772 long_options, NULL)) != -1) {
774 case 'f': /* force remove even if not owned by user */
777 case 'r': /* remove home dir and mailbox */
786 if ((optind + 1) != argc) {
792 #ifdef ACCT_TOOLS_SETUID
795 struct passwd *pampw;
796 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
799 _("%s: Cannot determine your user name.\n"),
804 retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh);
807 if (PAM_SUCCESS == retval) {
808 retval = pam_authenticate (pamh, 0);
811 if (PAM_SUCCESS == retval) {
812 retval = pam_acct_mgmt (pamh, 0);
816 (void) pam_end (pamh, retval);
818 if (PAM_SUCCESS != retval) {
819 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
823 #endif /* ACCT_TOOLS_SETUID */
825 is_shadow_pwd = spw_file_present ();
827 is_shadow_grp = sgr_file_present ();
831 * Start with a quick check to see if the user exists.
833 user_name = argv[argc - 1];
836 pwd = getpwnam (user_name); /* local, no need for xgetpwnam */
838 fprintf (stderr, _("%s: user '%s' does not exist\n"),
841 audit_logger (AUDIT_DEL_USER, Prog,
842 "deleting user not found",
843 user_name, AUDIT_NO_ID,
844 SHADOW_AUDIT_FAILURE);
848 user_id = pwd->pw_uid;
849 user_home = xstrdup (pwd->pw_dir);
854 * Now make sure it isn't an NIS user.
861 _("%s: user %s is a NIS user\n"), Prog, user_name);
862 if ( !yp_get_default_domain (&nis_domain)
863 && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
865 _("%s: %s is the NIS master\n"),
872 * Check to make certain the user isn't logged in.
873 * Note: This is a best effort basis. The user may log in between,
874 * a cron job may be started on her behalf, etc.
876 if (user_busy (user_name, user_id) != 0) {
878 _("%s: user %s is currently logged in\n"),
882 audit_logger (AUDIT_DEL_USER, Prog,
883 "deleting user logged in",
884 user_name, AUDIT_NO_ID,
885 SHADOW_AUDIT_FAILURE);
892 * Do the hard stuff - open the files, create the user entries,
893 * create the home directory, then close and update the files.
900 errors += remove_mailbox ();
903 int home_owned = is_owner (user_id, user_home);
904 if (-1 == home_owned) {
906 _("%s: %s home directory (%s) not found\n"),
907 Prog, user_name, user_home);
909 } else if ((0 == home_owned) && !fflg) {
911 _("%s: %s not owned by %s, not removing\n"),
912 Prog, user_home, user_name);
919 #ifdef EXTRA_CHECK_HOME_DIR
920 /* This may be slow, the above should be good enough. */
924 * For safety, refuse to remove the home directory if it
925 * would result in removing some other user's home
926 * directory. Still not perfect so be careful, but should
927 * prevent accidents if someone has /home or / as home
928 * directory... --marekm
931 while ((pwd = getpwent ())) {
932 if (strcmp (pwd->pw_name, user_name) == 0) {
935 if (path_prefix (user_home, pwd->pw_dir)) {
938 ("%s: not removing directory %s (would remove home of user %s)\n"),
939 Prog, user_home, pwd->pw_name);
951 if (remove_tree (user_home) != 0) {
953 _("%s: error removing directory %s\n"),
961 audit_logger (AUDIT_DEL_USER, Prog,
962 "deleting home directory",
963 user_name, (unsigned int) user_id,
964 SHADOW_AUDIT_SUCCESS);
970 audit_logger (AUDIT_DEL_USER, Prog,
971 "deleting home directory",
972 user_name, AUDIT_NO_ID,
973 SHADOW_AUDIT_FAILURE);
978 if (is_selinux_enabled () > 0) {
980 args[0] = "/usr/sbin/semanage";
985 safe_system (args[0], args, NULL, 1);
990 * Cancel any crontabs or at jobs. Have to do this before we remove
991 * the entry from /etc/passwd.
993 user_cancel (user_name);
996 nscd_flush_cache ("passwd");
997 nscd_flush_cache ("group");
999 return ((0 != errors) ? E_HOMEDIR : E_SUCCESS);