int main(int argc, char **argv) { struct passwd *pwd; int arg; int errors = 0; /* * Get my name so that I can use it to report errors. */ Prog = Basename(argv[0]); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH); #ifdef SHADOWPWD is_shadow_pwd = spw_file_present(); #endif #ifdef SHADOWGRP is_shadow_grp = sgr_file_present(); #endif /* * The open routines for the DBM files don't use read-write * as the mode, so we have to clue them in. */ #ifdef NDBM pw_dbm_mode = O_RDWR; #ifdef SHADOWPWD sp_dbm_mode = O_RDWR; #endif gr_dbm_mode = O_RDWR; #ifdef SHADOWGRP sg_dbm_mode = O_RDWR; #endif #endif while ((arg = getopt (argc, argv, "fr")) != EOF) { switch (arg) { case 'f': /* force remove even if not owned by user */ fflg++; break; case 'r': /* remove home dir and mailbox */ rflg++; break; default: usage(); } } if (optind + 1 != argc) usage (); /* * Start with a quick check to see if the user exists. */ user_name = argv[argc - 1]; if (! (pwd = getpwnam (user_name))) { fprintf(stderr, _("%s: user %s does not exist\n"), Prog, user_name); exit(E_NOTFOUND); } #ifdef USE_NIS /* * Now make sure it isn't an NIS user. */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf(stderr, _("%s: user %s is a NIS user\n"), Prog, user_name); if (! yp_get_default_domain (&nis_domain) && ! yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf(stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit(E_NOTFOUND); } #endif user_id = pwd->pw_uid; user_home = xstrdup(pwd->pw_dir); /* * Check to make certain the user isn't logged in. */ user_busy (user_name, user_id); /* * Do the hard stuff - open the files, create the user entries, * create the home directory, then close and update the files. */ open_files (); update_user (); update_groups (); #ifndef NO_REMOVE_MAILBOX if (rflg) remove_mailbox(); #endif if (rflg && !fflg && !is_owner(user_id, user_home)) { fprintf(stderr, _("%s: %s not owned by %s, not removing\n"), Prog, user_home, user_name); rflg = 0; errors++; } /* This may be slow, the above should be good enough. */ #ifdef EXTRA_CHECK_HOME_DIR if (rflg && !fflg) { /* * For safety, refuse to remove the home directory * if it would result in removing some other user's * home directory. Still not perfect so be careful, * but should prevent accidents if someone has /home * or / as home directory... --marekm */ setpwent(); while ((pwd = getpwent())) { if (strcmp(pwd->pw_name, user_name) == 0) continue; if (path_prefix(user_home, pwd->pw_dir)) { fprintf(stderr, _("%s: not removing directory %s (would remove home of user %s)\n"), Prog, user_home, pwd->pw_name); rflg = 0; errors++; break; } } } #endif if (rflg) { if (remove_tree(user_home) || rmdir(user_home)) { fprintf(stderr, _("%s: error removing directory %s\n"), Prog, user_home); errors++; } } /* * Cancel any crontabs or at jobs. Have to do this before we * remove the entry from /etc/passwd. */ user_cancel(user_name); close_files (); exit(errors ? E_HOMEDIR : E_SUCCESS); /*NOTREACHED*/ }
/* * main - userdel command */ int main (int argc, char **argv) { int errors = 0; /* Error in the removal of the home directory */ #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("userdel"); #ifdef WITH_AUDIT audit_help_open (); #endif /* WITH_AUDIT */ { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"force", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"remove", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, #ifdef WITH_SELINUX {"selinux-user", no_argument, NULL, 'Z'}, #endif /* WITH_SELINUX */ {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, #ifdef WITH_SELINUX "fhrR:Z", #else /* !WITH_SELINUX */ "fhrR:", #endif /* !WITH_SELINUX */ long_options, NULL)) != -1) { switch (c) { case 'f': /* force remove even if not owned by user */ fflg = true; break; case 'h': usage (E_SUCCESS); break; case 'r': /* remove home dir and mailbox */ rflg = true; break; case 'R': /* no-op, handled in process_root_flag () */ break; #ifdef WITH_SELINUX case 'Z': if (is_selinux_enabled () > 0) { Zflg = true; } else { fprintf (stderr, _("%s: -Z requires SELinux enabled kernel\n"), Prog); exit (E_BAD_ARG); } break; #endif /* WITH_SELINUX */ default: usage (E_USAGE); } } } if ((optind + 1) != argc) { usage (E_USAGE); } #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (pampw == NULL) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (E_PW_UPDATE); } retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh); } if (PAM_SUCCESS == retval) { retval = pam_authenticate (pamh, 0); } if (PAM_SUCCESS == retval) { retval = pam_acct_mgmt (pamh, 0); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM: %s\n"), Prog, pam_strerror (pamh, retval)); SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval))); if (NULL != pamh) { (void) pam_end (pamh, retval); } exit (E_PW_UPDATE); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ is_shadow_pwd = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif /* SHADOWGRP */ #ifdef ENABLE_SUBIDS is_sub_uid = sub_uid_file_present (); is_sub_gid = sub_gid_file_present (); #endif /* ENABLE_SUBIDS */ /* * Start with a quick check to see if the user exists. */ user_name = argv[argc - 1]; { struct passwd *pwd; pwd = getpwnam (user_name); /* local, no need for xgetpwnam */ if (NULL == pwd) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user not found", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ exit (E_NOTFOUND); } user_id = pwd->pw_uid; user_gid = pwd->pw_gid; user_home = xstrdup (pwd->pw_dir); } #ifdef WITH_TCB if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { exit (E_NOTFOUND); } #endif /* WITH_TCB */ #ifdef USE_NIS /* * Now make sure it isn't an NIS user. */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: user %s is a NIS user\n"), Prog, user_name); if ( !yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif /* USE_NIS */ /* * Check to make certain the user isn't logged in. * Note: This is a best effort basis. The user may log in between, * a cron job may be started on her behalf, etc. */ if (user_busy (user_name, user_id) != 0) { if (!fflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user logged in", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ exit (E_USER_BUSY); } } /* * Do the hard stuff - open the files, create the user entries, * create the home directory, then close and update the files. */ open_files (); update_user (); update_groups (); if (rflg) { errors += remove_mailbox (); } if (rflg) { int home_owned = is_owner (user_id, user_home); if (-1 == home_owned) { fprintf (stderr, _("%s: %s home directory (%s) not found\n"), Prog, user_name, user_home); rflg = 0; } else if ((0 == home_owned) && !fflg) { fprintf (stderr, _("%s: %s not owned by %s, not removing\n"), Prog, user_home, user_name); rflg = 0; errors++; /* continue */ } } #ifdef EXTRA_CHECK_HOME_DIR /* This may be slow, the above should be good enough. */ if (rflg && !fflg) { struct passwd *pwd; /* * For safety, refuse to remove the home directory if it * would result in removing some other user's home * directory. Still not perfect so be careful, but should * prevent accidents if someone has /home or / as home * directory... --marekm */ setpwent (); while ((pwd = getpwent ())) { if (strcmp (pwd->pw_name, user_name) == 0) { continue; } if (path_prefix (user_home, pwd->pw_dir)) { fprintf (stderr, _("%s: not removing directory %s (would remove home of user %s)\n"), Prog, user_home, pwd->pw_name); rflg = false; errors++; /* continue */ break; } } endpwent (); } #endif /* EXTRA_CHECK_HOME_DIR */ if (rflg) { if (remove_tree (user_home, true) != 0) { fprintf (stderr, _("%s: error removing directory %s\n"), Prog, user_home); errors++; /* continue */ } #ifdef WITH_AUDIT else { audit_logger (AUDIT_DEL_USER, Prog, "deleting home directory", user_name, (unsigned int) user_id, SHADOW_AUDIT_SUCCESS); } #endif /* WITH_AUDIT */ } #ifdef WITH_AUDIT if (0 != errors) { audit_logger (AUDIT_DEL_USER, Prog, "deleting home directory", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); } #endif /* WITH_AUDIT */ #ifdef WITH_SELINUX if (Zflg) { if (del_seuser (user_name) != 0) { fprintf (stderr, _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_ADD_USER, Prog, "removing SELinux user mapping", user_name, (unsigned int) user_id, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ fail_exit (E_SE_UPDATE); } } #endif /* WITH_SELINUX */ /* * Cancel any crontabs or at jobs. Have to do this before we remove * the entry from /etc/passwd. */ user_cancel (user_name); close_files (); #ifdef WITH_TCB errors += remove_tcbdir (user_name, user_id); #endif /* WITH_TCB */ nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); return ((0 != errors) ? E_HOMEDIR : E_SUCCESS); }