static int context_update_kernel_hostname(Context *c) { const char *static_hn; const char *hn; assert(c); static_hn = c->data[PROP_STATIC_HOSTNAME]; /* /etc/hostname with something other than "localhost" * has the highest preference ... */ if (hostname_is_useful(static_hn)) hn = static_hn; /* ... the transient host name, (ie: DHCP) comes next ... */ else if (!isempty(c->data[PROP_HOSTNAME])) hn = c->data[PROP_HOSTNAME]; /* ... fallback to static "localhost.*" ignored above ... */ else if (!isempty(static_hn)) hn = static_hn; /* ... and the ultimate fallback */ else hn = FALLBACK_HOSTNAME; if (sethostname_idempotent(hn) < 0) return -errno; (void) nscd_flush_cache(STRV_MAKE("hosts")); return 0; }
/* * grpck - verify group file integrity */ int main (int argc, char **argv) { int errors = 0; bool changed = false; /* * 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 ("grpck"); /* Parse the command line arguments */ process_flags (argc, argv); open_files (); if (sort_mode) { gr_sort (); #ifdef SHADOWGRP if (is_shadow) { sgr_sort (); } changed = true; #endif } else { check_grp_file (&errors, &changed); #ifdef SHADOWGRP if (is_shadow) { check_sgr_file (&errors, &changed); } #endif } /* Commit the change in the database if needed */ close_files (changed); nscd_flush_cache ("group"); /* * Tell the user what we did and exit. */ if (0 != errors) { if (changed) { printf (_("%s: the files have been updated\n"), Prog); } else { printf (_("%s: no changes\n"), Prog); } } return ((0 != errors) ? E_BAD_ENTRY : E_OKAY); }
/* * main - groupadd command */ int main (int argc, char **argv) { /* * 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); prefix = process_prefix_flag ("-P", argc, argv); OPENLOG ("groupadd"); #ifdef WITH_AUDIT audit_help_open (); #endif if (atexit (do_cleanups) != 0) { fprintf (stderr, _("%s: Cannot setup cleanup service.\n"), Prog); exit (1); } /* * Parse the command line options. */ process_flags (argc, argv); check_perms (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif /* * Do the hard stuff - open the files, create the group entries, * then close and update the files. */ open_files (); if (!gflg) { if (find_new_gid (rflg, &group_id, NULL) < 0) { exit (E_GID_IN_USE); } } grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
static void smb_nscd_flush_cache(const char *service) { #ifdef HAVE_NSCD_FLUSH_CACHE if (nscd_flush_cache(service)) { DEBUG(10,("failed to flush nscd cache for '%s' service: %s. " "Is nscd running?\n", service, strerror(errno))); } #endif }
static void dec_lock_count(void) { if (lock_count > 0) { lock_count--; if (lock_count == 0) { /* Tell nscd when lock count goes to zero, if any of the files were changed. */ if (nscd_need_reload) { nscd_flush_cache("passwd"); nscd_flush_cache("group"); nscd_need_reload = 0; } #ifdef HAVE_LCKPWDF ulckpwdf(); #endif } } }
int main (int argc, char **argv) { const struct passwd *pw; struct passwd pwent; const struct spwd *sp; struct spwd spent; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("pwconv"); process_flags (argc, argv); #ifdef WITH_TCB if (getdef_bool("USE_TCB")) { fprintf (stderr, _("%s: can't work with tcb enabled\n"), Prog); exit (E_FAILURE); } #endif /* WITH_TCB */ if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); fail_exit (E_PWDBUSY); } pw_locked = true; if (pw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); fail_exit (E_MISSING); } if (spw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname ()); fail_exit (E_PWDBUSY); } spw_locked = true; if (spw_open (O_CREAT | O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); fail_exit (E_FAILURE); } /* * Remove /etc/shadow entries for users not in /etc/passwd. */ (void) spw_rewind (); while ((sp = spw_next ()) != NULL) { if (pw_locate (sp->sp_namp) != NULL) { continue; } if (spw_remove (sp->sp_namp) == 0) { /* * This shouldn't happen (the entry exists) but... */ fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, sp->sp_namp, spw_dbname ()); fail_exit (E_FAILURE); } } /* * Update shadow entries which don't have "x" as pw_passwd. Add any * missing shadow entries. */ (void) pw_rewind (); while ((pw = pw_next ()) != NULL) { sp = spw_locate (pw->pw_name); if (NULL != sp) { /* do we need to update this entry? */ if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { continue; } /* update existing shadow entry */ spent = *sp; } else { /* add new shadow entry */ memset (&spent, 0, sizeof spent); spent.sp_namp = pw->pw_name; spent.sp_min = getdef_num ("PASS_MIN_DAYS", -1); spent.sp_max = getdef_num ("PASS_MAX_DAYS", -1); spent.sp_warn = getdef_num ("PASS_WARN_AGE", -1); spent.sp_inact = -1; spent.sp_expire = -1; spent.sp_flag = SHADOW_SP_FLAG_UNSET; } spent.sp_pwdp = pw->pw_passwd; spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password * change */ spent.sp_lstchg = -1; } if (spw_update (&spent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, spw_dbname (), spent.sp_namp); fail_exit (E_FAILURE); } /* remove password from /etc/passwd */ pwent = *pw; pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ if (pw_update (&pwent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); fail_exit (E_FAILURE); } } if (spw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); fail_exit (E_FAILURE); } if (pw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); fail_exit (E_FAILURE); } /* /etc/passwd- (backup file) */ if (chmod (PASSWD_FILE "-", 0600) != 0) { fprintf (stderr, _("%s: failed to change the mode of %s to 0600\n"), Prog, PASSWD_FILE "-"); SYSLOG ((LOG_ERR, "failed to change the mode of %s to 0600", PASSWD_FILE "-")); /* continue */ } if (pw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); /* continue */ } if (spw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); /* continue */ } nscd_flush_cache ("passwd"); return E_SUCCESS; }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; char *newpwd; char *cp; #ifdef SHADOWGRP const struct sgrp *sg; struct sgrp newsg; #endif const struct group *gr; struct group newgr; int errors = 0; int line = 0; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); process_flags (argc, argv); OPENLOG ("chgpasswd"); check_perms (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif open_files (); /* * Read each line, separating the group name from the password. The * group entry for each group will be looked up in the appropriate * file (gshadow or group) and the password changed. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } /* * The group's name is the first field. It is separated from * the password with a ":" character which is replaced with a * NUL to give the new password. The new password will then * be encrypted in the normal fashion with a new salt * generated, unless the '-e' is given, in which case it is * assumed to already be encrypted. */ name = buf; cp = strchr (name, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { fprintf (stderr, _("%s: line %d: missing new password\n"), Prog, line); errors++; continue; } newpwd = cp; if ( (!eflg) && ( (NULL == crypt_method) || (0 != strcmp (crypt_method, "NONE")))) { void *arg = NULL; const char *salt; if (md5flg) { crypt_method = "MD5"; } #ifdef USE_SHA_CRYPT if (sflg) { arg = &sha_rounds; } #endif salt = crypt_make_salt (crypt_method, arg); cp = pw_encrypt (newpwd, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); fail_exit (1); } } /* * Get the group file entry for this group. The group must * already exist. */ gr = gr_locate (name); if (NULL == gr) { fprintf (stderr, _("%s: line %d: group '%s' does not exist\n"), Prog, line, name); errors++; continue; } #ifdef SHADOWGRP if (is_shadow_grp) { /* The gshadow entry should be updated if the * group entry has a password set to 'x'. * But on the other hand, if there is already both * a group and a gshadow password, it's preferable * to update both. */ sg = sgr_locate (name); if ( (NULL == sg) && (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) == 0)) { static char *empty = NULL; /* If the password is set to 'x' in * group, but there are no entries in * gshadow, create one. */ newsg.sg_name = name; /* newsg.sg_passwd = NULL; will be set later */ newsg.sg_adm = ∅ newsg.sg_mem = dup_list (gr->gr_mem); sg = &newsg; } } else { sg = NULL; } #endif /* * The freshly encrypted new password is merged into the * group's entry. */ #ifdef SHADOWGRP if (NULL != sg) { newsg = *sg; newsg.sg_passwd = cp; } if ( (NULL == sg) || (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)) #endif { newgr = *gr; newgr.gr_passwd = cp; } /* * The updated group file entry is then put back and will * be written to the group file later, after all the * other entries have been updated as well. */ #ifdef SHADOWGRP if (NULL != sg) { if (sgr_update (&newsg) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, sgr_dbname (), newsg.sg_name); errors++; continue; } } if ( (NULL == sg) || (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)) #endif { if (gr_update (&newgr) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, gr_dbname (), newgr.gr_name); errors++; continue; } } } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the group file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. */ if (0 != errors) { fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); fail_exit (1); } close_files (); nscd_flush_cache ("group"); return (0); }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; char *newpwd; char *cp; #ifndef USE_PAM const struct spwd *sp; struct spwd newsp; const struct passwd *pw; struct passwd newpw; #endif /* !USE_PAM */ int errors = 0; int line = 0; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_flags (argc, argv); OPENLOG ("chpasswd"); check_perms (); #ifndef USE_PAM is_shadow_pwd = spw_file_present (); open_files (); #endif /* * Read each line, separating the user name from the password. The * password entry for each user will be looked up in the appropriate * file (shadow or passwd) and the password changed. For shadow * files the last change date is set directly, for passwd files the * last change date is set in the age only if aging information is * present. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { if (feof (stdin) == 0) { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } } /* * The username is the first field. It is separated from the * password with a ":" character which is replaced with a * NUL to give the new password. The new password will then * be encrypted in the normal fashion with a new salt * generated, unless the '-e' is given, in which case it is * assumed to already be encrypted. */ name = buf; cp = strchr (name, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { fprintf (stderr, _("%s: line %d: missing new password\n"), Prog, line); errors++; continue; } newpwd = cp; #ifdef USE_PAM if (do_pam_passwd_non_interractive ("chpasswd", name, newpwd) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, line, name); errors++; } #else /* !USE_PAM */ if ( !eflg && ( (NULL == crypt_method) || (0 != strcmp (crypt_method, "NONE")))) { void *arg = NULL; if (md5flg) { crypt_method = "MD5"; } else if (crypt_method != NULL) { #ifdef USE_SHA_CRYPT if (sflg) { arg = &sha_rounds; } #endif } else { crypt_method = NULL; } cp = pw_encrypt (newpwd, crypt_make_salt(crypt_method, arg)); } /* * Get the password file entry for this user. The user must * already exist. */ pw = pw_locate (name); if (NULL == pw) { fprintf (stderr, _("%s: line %d: user '%s' does not exist\n"), Prog, line, name); errors++; continue; } if (is_shadow_pwd) { sp = spw_locate (name); } else { sp = NULL; } /* * The freshly encrypted new password is merged into the * user's password file entry and the last password change * date is set to the current date. */ if (NULL != sp) { newsp = *sp; newsp.sp_pwdp = cp; newsp.sp_lstchg = (long) time ((time_t *)NULL) / SCALE; if (0 == newsp.sp_lstchg) { /* Better disable aging than requiring a * password change */ newsp.sp_lstchg = -1; } } else { newpw = *pw; newpw.pw_passwd = cp; } /* * The updated password file entry is then put back and will * be written to the password file later, after all the * other entries have been updated as well. */ if (NULL != sp) { if (spw_update (&newsp) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, spw_dbname (), newsp.sp_namp); errors++; continue; } } else { if (pw_update (&newpw) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, pw_dbname (), newpw.pw_name); errors++; continue; } } #endif /* !USE_PAM */ } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the password file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. * * With PAM, it is not possible to delay the update of the * password database. */ if (0 != errors) { #ifndef USE_PAM fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); #endif fail_exit (1); } #ifndef USE_PAM /* Save the changes */ close_files (); #endif nscd_flush_cache ("passwd"); return (0); }
int main (int argc, char **argv) { const struct group *gr; struct group grent; const struct sgrp *sg; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("grpunconv"); process_flags (argc, argv); if (sgr_file_present () == 0) { exit (0); /* no /etc/gshadow, nothing to do */ } if (gr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, gr_dbname ()); fail_exit (5); } gr_locked = true; if (gr_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); fail_exit (1); } if (sgr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, sgr_dbname ()); fail_exit (5); } sgr_locked = true; if (sgr_open (O_RDONLY) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); fail_exit (1); } /* * Update group passwords if non-shadow password is "x". */ (void) gr_rewind (); while ((gr = gr_next ()) != NULL) { sg = sgr_locate (gr->gr_name); if ( (NULL != sg) && (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) == 0)) { /* add password to /etc/group */ grent = *gr; grent.gr_passwd = sg->sg_passwd; if (gr_update (&grent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grent.gr_name); fail_exit (3); } } } (void) sgr_close (); /* was only open O_RDONLY */ if (gr_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); fail_exit (3); } if (unlink (SGROUP_FILE) != 0) { fprintf (stderr, _("%s: cannot delete %s\n"), Prog, SGROUP_FILE); SYSLOG ((LOG_ERR, "cannot delete %s", SGROUP_FILE)); fail_exit (3); } if (gr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); /* continue */ } if (sgr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); /* continue */ } nscd_flush_cache ("group"); return 0; }
/* * pwck - verify password file integrity */ int main (int argc, char **argv) { int errors = 0; bool changed = false; /* * 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); OPENLOG ("pwck"); /* Parse the command line arguments */ process_flags (argc, argv); open_files (); if (sort_mode) { if (pw_sort () != 0) { fprintf (stderr, _("%s: cannot sort entries in %s\n"), Prog, pw_dbname ()); fail_exit (E_CANTSORT); } if (is_shadow) { if (spw_sort () != 0) { fprintf (stderr, _("%s: cannot sort entries in %s\n"), Prog, spw_dbname ()); fail_exit (E_CANTSORT); } } changed = true; } else { check_pw_file (&errors, &changed); if (is_shadow) { check_spw_file (&errors, &changed); } } close_files (changed); nscd_flush_cache ("passwd"); /* * Tell the user what we did and exit. */ if (0 != errors) { printf (changed ? _("%s: the files have been updated\n") : _("%s: no changes\n"), Prog); } closelog (); return ((0 != errors) ? E_BADENTRY : E_OKAY); }
/* * main - groupmod command * */ int main (int argc, char **argv) { #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 ("groupmod"); #ifdef WITH_AUDIT audit_help_open (); #endif if (atexit (do_cleanups) != 0) { fprintf (stderr, _("%s: Cannot setup cleanup service.\n"), Prog); exit (1); } process_flags (argc, argv); #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (NULL == pampw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (1); } retval = pam_start ("groupmod", 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 (1); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif { struct group *grp; /* * Start with a quick check to see if the group exists. */ grp = getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), Prog, group_name); exit (E_NOTFOUND); } else { group_id = grp->gr_gid; } } #ifdef USE_NIS /* * Now make sure it isn't an NIS group. */ if (__isgrNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: group %s is a NIS group\n"), Prog, group_name); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif if (gflg) { check_new_gid (); } if (nflg) { check_new_name (); } lock_files (); /* * Now if the group is not changed, it's our fault. * Make sure failures will be reported. */ prepare_failure_reports (); /* * Do the hard stuff - open the files, create the group entries, * then close and update the files. */ open_files (); grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
int main (int argc, char **argv) { bool editshadow = false; char *a; bool do_vipw; (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); progname = ((a = strrchr (*argv, '/')) ? a + 1 : *argv); do_vipw = (strcmp (progname, "vigr") != 0); OPENLOG (do_vipw ? "vipw" : "vigr"); { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"group", no_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, {"passwd", no_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"shadow", no_argument, NULL, 's'}, #ifdef WITH_TCB {"user", required_argument, NULL, 'u'}, #endif /* WITH_TCB */ {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, #ifdef WITH_TCB "ghpqsu:", #else /* !WITH_TCB */ "ghpqs", #endif /* !WITH_TCB */ long_options, NULL)) != -1) { switch (c) { case 'g': do_vipw = false; break; case 'h': usage (E_SUCCESS); break; case 'p': do_vipw = true; break; case 'q': quiet = true; break; case 's': editshadow = true; break; #ifdef WITH_TCB case 'u': user = optarg; break; #endif /* WITH_TCB */ default: usage (E_USAGE); } } } if (do_vipw) { if (editshadow) { #ifdef WITH_TCB if (getdef_bool ("USE_TCB") && (NULL != user)) { if (shadowtcb_set_user (user) == SHADOWTCB_FAILURE) { fprintf (stderr, _("%s: failed to find tcb directory for %s\n"), progname, user); return E_SHADOW_NOTFOUND; } tcb_mode = true; } #endif /* WITH_TCB */ vipwedit (spw_dbname (), spw_lock, spw_unlock); printf (MSG_WARN_EDIT_OTHER_FILE, spw_dbname (), pw_dbname (), "vipw"); } else { vipwedit (pw_dbname (), pw_lock, pw_unlock); if (spw_file_present ()) { printf (MSG_WARN_EDIT_OTHER_FILE, pw_dbname (), spw_dbname (), "vipw -s"); } } } else { #ifdef SHADOWGRP if (editshadow) { vipwedit (sgr_dbname (), sgr_lock, sgr_unlock); printf (MSG_WARN_EDIT_OTHER_FILE, sgr_dbname (), gr_dbname (), "vigr"); } else { #endif /* SHADOWGRP */ vipwedit (gr_dbname (), gr_lock, gr_unlock); #ifdef SHADOWGRP if (sgr_file_present ()) { printf (MSG_WARN_EDIT_OTHER_FILE, gr_dbname (), sgr_dbname (), "vigr -s"); } } #endif /* SHADOWGRP */ } nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); return E_SUCCESS; }
/* * 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); }
int main (int argc, char **argv) { const struct group *gr; struct group grent; const struct sgrp *sg; struct sgrp sgent; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("grpconv"); process_flags (argc, argv); if (gr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, gr_dbname ()); fail_exit (5); } gr_locked = true; if (gr_open (O_CREAT | O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); fail_exit (1); } if (sgr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, sgr_dbname ()); fail_exit (5); } sgr_locked = true; if (sgr_open (O_CREAT | O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); fail_exit (1); } /* * Remove /etc/gshadow entries for groups not in /etc/group. */ (void) sgr_rewind (); while ((sg = sgr_next ()) != NULL) { if (gr_locate (sg->sg_name) != NULL) { continue; } if (sgr_remove (sg->sg_name) == 0) { /* * This shouldn't happen (the entry exists) but... */ fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, sg->sg_name, sgr_dbname ()); fail_exit (3); } } /* * Update shadow group passwords if non-shadow password is not "x". * Add any missing shadow group entries. */ (void) gr_rewind (); while ((gr = gr_next ()) != NULL) { sg = sgr_locate (gr->gr_name); if (NULL != sg) { /* update existing shadow group entry */ sgent = *sg; if (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0) sgent.sg_passwd = gr->gr_passwd; } else { static char *empty = 0; /* add new shadow group entry */ memset (&sgent, 0, sizeof sgent); sgent.sg_name = gr->gr_name; sgent.sg_passwd = gr->gr_passwd; sgent.sg_adm = ∅ } /* * XXX - sg_mem is redundant, it is currently always a copy * of gr_mem. Very few programs actually use sg_mem, and all * of them are in the shadow suite. Maybe this field could * be used for something else? Any suggestions? */ sgent.sg_mem = gr->gr_mem; if (sgr_update (&sgent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, sgr_dbname (), sgent.sg_name); fail_exit (3); } /* remove password from /etc/group */ grent = *gr; grent.gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ if (gr_update (&grent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grent.gr_name); fail_exit (3); } } if (sgr_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); fail_exit (3); } if (gr_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); fail_exit (3); } if (sgr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); /* continue */ } if (gr_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); /* continue */ } nscd_flush_cache ("group"); return 0; }
/* * chfn - change a user's password file information * * This command controls the GECOS field information in the password * file entry. * * The valid options are * * -f full name * -r room number * -w work phone number * -h home phone number * -o other information (*) * * (*) requires root permission to execute. */ int main (int argc, char **argv) { const struct passwd *pw; /* password file entry */ char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */ char *user; /* * Get the program name. The program name is used as a * prefix to most error messages. */ Prog = Basename (argv[0]); sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); /* * This command behaves different for root and non-root * users. */ amroot = (getuid () == 0); OPENLOG ("chfn"); /* parse the command line options */ process_flags (argc, argv); /* * Get the name of the user to check. It is either the command line * name, or the name getlogin() returns. */ if (optind < argc) { user = argv[optind]; pw = xgetpwnam (user); if (NULL == pw) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user); fail_exit (E_NOPERM); } } else { pw = get_my_pwent (); if (NULL == pw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid ())); fail_exit (E_NOPERM); } user = xstrdup (pw->pw_name); } #ifdef USE_NIS /* * Now we make sure this is a LOCAL password entry for this user ... */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: cannot change user '%s' on NIS client.\n"), Prog, user); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _ ("%s: '%s' is the NIS master for this client.\n"), Prog, nis_master); } fail_exit (E_NOPERM); } #endif /* Check that the caller is allowed to change the gecos of the * specified user */ check_perms (pw); /* If some fields were not set on the command line, load the value from * the old gecos fields. */ get_old_fields (pw->pw_gecos); /* * If none of the fields were changed from the command line, let the * user interactively change them. */ if (!fflg && !rflg && !wflg && !hflg && !oflg) { printf (_("Changing the user information for %s\n"), user); new_fields (); } /* * Check all of the fields for valid information */ check_fields (); /* * Build the new GECOS field by plastering all the pieces together, * if they will fit ... */ if ((strlen (fullnm) + strlen (roomno) + strlen (workph) + strlen (homeph) + strlen (slop)) > (unsigned int) 80) { fprintf (stderr, _("%s: fields too long\n"), Prog); fail_exit (E_NOPERM); } snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s", fullnm, roomno, workph, homeph, ('\0' != slop[0]) ? "," : "", slop); /* Rewrite the user's gecos in the passwd file */ update_gecos (user, new_gecos); SYSLOG ((LOG_INFO, "changed user '%s' information", user)); nscd_flush_cache ("passwd"); closelog (); exit (E_SUCCESS); }
/* * gpasswd - administer the /etc/group file */ int main (int argc, char **argv) { struct group grent; #ifdef SHADOWGRP struct sgrp sgent; #endif struct passwd *pw = NULL; #ifdef WITH_AUDIT audit_help_open (); #endif sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* * Make a note of whether or not this command was invoked by root. * This will be used to bypass certain checks later on. Also, set * the real user ID to match the effective user ID. This will * prevent the invoker from issuing signals which would interfere * with this command. */ bywho = getuid (); Prog = Basename (argv[0]); OPENLOG ("gpasswd"); setbuf (stdout, NULL); setbuf (stderr, NULL); #ifdef SHADOWGRP is_shadowgrp = sgr_file_present (); #endif /* * Determine the name of the user that invoked this command. This * is really hit or miss because there are so many ways that command * can be executed and so many ways to trip up the routines that * report the user name. */ pw = get_my_pwent (); if (NULL == pw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid ())); exit (E_NOPERM); } myname = xstrdup (pw->pw_name); /* * Register an exit function to warn for any inconsistency that we * could create. */ if (atexit (do_cleanups) != 0) { fprintf(stderr, "%s: cannot set exit function\n", Prog); exit (1); } /* Parse the options */ process_flags (argc, argv); /* * Replicate the group so it can be modified later on. */ #ifdef SHADOWGRP get_group (&grent, &sgent); #else get_group (&grent); #endif /* * Check if the user is allowed to change the password of this group. */ #ifdef SHADOWGRP check_perms (&grent, &sgent); #else check_perms (&grent); #endif /* * Removing a password is straight forward. Just set the password * field to a "". */ if (rflg) { grent.gr_passwd = ""; /* XXX warning: const */ #ifdef SHADOWGRP sgent.sg_passwd = ""; /* XXX warning: const */ #endif goto output; } else if (Rflg) { /* * Same thing for restricting the group. Set the password * field to "!". */ grent.gr_passwd = "!"; /* XXX warning: const */ #ifdef SHADOWGRP sgent.sg_passwd = "!"; /* XXX warning: const */ #endif goto output; } /* * Adding a member to a member list is pretty straightforward as * well. Call the appropriate routine and split. */ if (aflg) { printf (_("Adding user %s to group %s\n"), user, group); grent.gr_mem = add_list (grent.gr_mem, user); #ifdef SHADOWGRP if (is_shadowgrp) { sgent.sg_mem = add_list (sgent.sg_mem, user); } #endif goto output; } /* * Removing a member from the member list is the same deal as adding * one, except the routine is different. */ if (dflg) { bool removed = false; printf (_("Removing user %s from group %s\n"), user, group); if (is_on_list (grent.gr_mem, user)) { removed = true; grent.gr_mem = del_list (grent.gr_mem, user); } #ifdef SHADOWGRP if (is_shadowgrp) { if (is_on_list (sgent.sg_mem, user)) { removed = true; sgent.sg_mem = del_list (sgent.sg_mem, user); } } #endif if (!removed) { fprintf (stderr, _("%s: user '%s' is not a member of '%s'\n"), Prog, user, group); exit (E_BAD_ARG); } goto output; } #ifdef SHADOWGRP /* * Replacing the entire list of administrators is simple. Check the * list to make sure everyone is a real user. Then slap the new list * in place. */ if (Aflg) { sgent.sg_adm = comma_to_list (admins); if (!Mflg) { goto output; } } #endif /* SHADOWGRP */ /* * Replacing the entire list of members is simple. Check the list to * make sure everyone is a real user. Then slap the new list in * place. */ if (Mflg) { #ifdef SHADOWGRP sgent.sg_mem = comma_to_list (members); #endif grent.gr_mem = comma_to_list (members); goto output; } /* * If the password is being changed, the input and output must both * be a tty. The typical keyboard signals are caught so the termio * modes can be restored. */ if ((isatty (0) == 0) || (isatty (1) == 0)) { fprintf (stderr, _("%s: Not a tty\n"), Prog); exit (E_NOPERM); } catch_signals (0); /* save tty modes */ (void) signal (SIGHUP, catch_signals); (void) signal (SIGINT, catch_signals); (void) signal (SIGQUIT, catch_signals); (void) signal (SIGTERM, catch_signals); #ifdef SIGTSTP (void) signal (SIGTSTP, catch_signals); #endif /* Prompt for the new password */ #ifdef SHADOWGRP change_passwd (&grent, &sgent); #else change_passwd (&grent); #endif /* * This is the common arrival point to output the new group file. * The freshly crafted entry is in allocated space. The group file * will be locked and opened for writing. The new entry will be * output, etc. */ output: if (setuid (0) != 0) { fputs (_("Cannot change ID to root.\n"), stderr); SYSLOG ((LOG_ERR, "can't setuid(0)")); closelog (); exit (E_NOPERM); } pwd_init (); open_files (); #ifdef SHADOWGRP update_group (&grent, &sgent); #else update_group (&grent); #endif close_files (); nscd_flush_cache ("group"); exit (E_SUCCESS); }
/* * passwd - change a user's password file information * * This command controls the password file and commands which are used * to modify it. * * The valid options are * * -d delete the password for the named account (*) * -e expire the password for the named account (*) * -f execute chfn command to interpret flags * -g execute gpasswd command to interpret flags * -i # set sp_inact to # days (*) * -k change password only if expired * -l lock the password of the named account (*) * -n # set sp_min to # days (*) * -r # change password in # repository * -s execute chsh command to interpret flags * -S show password status of named account * -u unlock the password of the named account (*) * -w # set sp_warn to # days (*) * -x # set sp_max to # days (*) * * (*) requires root permission to execute. * * All of the time fields are entered in days and converted to the * appropriate internal format. For finer resolute the chage * command must be used. */ int main (int argc, char **argv) { const struct passwd *pw; /* Password file entry for user */ #ifndef USE_PAM char *cp; /* Miscellaneous character pointing */ const struct spwd *sp; /* Shadow file entry for user */ #endif /* !USE_PAM */ (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* * The program behaves differently when executed by root than when * executed by a normal user. */ amroot = (getuid () == 0); /* * Get the program name. The program name is used as a prefix to * most error messages. */ Prog = Basename (argv[0]); sanitize_env (); OPENLOG ("passwd"); { /* * Parse the command line options. */ int option_index = 0; int c; static struct option long_options[] = { {"all", no_argument, NULL, 'a'}, {"delete", no_argument, NULL, 'd'}, {"expire", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"inactive", required_argument, NULL, 'i'}, {"keep-tokens", no_argument, NULL, 'k'}, {"lock", no_argument, NULL, 'l'}, {"mindays", required_argument, NULL, 'n'}, {"quiet", no_argument, NULL, 'q'}, {"root", required_argument, NULL, 'R'}, {"repository", required_argument, NULL, 'r'}, {"status", no_argument, NULL, 'S'}, {"unlock", no_argument, NULL, 'u'}, {"warndays", required_argument, NULL, 'w'}, {"maxdays", required_argument, NULL, 'x'}, {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, "adei:kln:qR:r:Suw:x:", long_options, &option_index)) != -1) { switch (c) { case 'a': aflg = true; break; case 'd': dflg = true; anyflag = true; break; case 'e': eflg = true; anyflag = true; break; case 'i': if ( (getlong (optarg, &inact) == 0) || (inact < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } iflg = true; anyflag = true; break; case 'k': /* change only if expired, like Linux-PAM passwd -k. */ kflg = true; /* ok for users */ break; case 'l': lflg = true; anyflag = true; break; case 'n': if ( (getlong (optarg, &age_min) == 0) || (age_min < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } nflg = true; anyflag = true; break; case 'q': qflg = true; /* ok for users */ break; case 'R': if ('/' != optarg[0]) { fprintf (stderr, _("%s: invalid chroot path '%s'\n"), Prog, optarg); exit (E_BAD_ARG); } newroot = optarg; if (access (newroot, F_OK) != 0) { fprintf(stderr, _("%s: chroot directory %s does not exist\n"), Prog, newroot); exit (E_BAD_ARG); } if ( chroot(newroot) != 0 ) { fprintf(stderr, _("%s: unable to chroot to directory %s\n"), Prog, newroot); exit (E_BAD_ARG); } break; case 'r': /* -r repository (files|nis|nisplus) */ /* only "files" supported for now */ if (strcmp (optarg, "files") != 0) { fprintf (stderr, _("%s: repository %s not supported\n"), Prog, optarg); exit (E_BAD_ARG); } break; case 'S': Sflg = true; /* ok for users */ break; case 'u': uflg = true; anyflag = true; break; case 'w': if ( (getlong (optarg, &warn) == 0) || (warn < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } wflg = true; anyflag = true; break; case 'x': if ( (getlong (optarg, &age_max) == 0) || (age_max < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } xflg = true; anyflag = true; break; default: usage (E_BAD_ARG); } } } /* * Now I have to get the user name. The name will be gotten from the * command line if possible. Otherwise it is figured out from the * environment. */ pw = get_my_pwent (); if (NULL == pw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid ())); exit (E_NOPERM); } myname = xstrdup (pw->pw_name); if (optind < argc) { name = argv[optind]; } else { name = myname; } /* * Make sure that at most one username was specified. */ if (argc > (optind+1)) { usage (E_USAGE); } /* * The -a flag requires -S, no other flags, no username, and * you must be root. --marekm */ if (aflg) { if (anyflag || !Sflg || (optind < argc)) { usage (E_USAGE); } if (!amroot) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } setpwent (); while ( (pw = getpwent ()) != NULL ) { print_status (pw); } endpwent (); exit (E_SUCCESS); } #if 0 /* * Allow certain users (administrators) to change passwords of * certain users. Not implemented yet. --marekm */ if (may_change_passwd (myname, name)) amroot = 1; #endif /* * If any of the flags were given, a user name must be supplied on * the command line. Only an unadorned command line doesn't require * the user's name be given. Also, -x, -n, -w, -i, -e, -d, * -l, -u may appear with each other. -S, -k must appear alone. */ /* * -S now ok for normal users (check status of my own account), and * doesn't require username. --marekm */ if (anyflag && optind >= argc) { usage (E_USAGE); } if ( (Sflg && kflg) || (anyflag && (Sflg || kflg))) { usage (E_USAGE); } if (anyflag && !amroot) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } pw = xgetpwnam (name); if (NULL == pw) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, name); exit (E_NOPERM); } #ifdef WITH_SELINUX /* only do this check when getuid()==0 because it's a pre-condition for changing a password without entering the old one */ if ((is_selinux_enabled() > 0) && (getuid() == 0) && (check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) { security_context_t user_context = NULL; const char *user = "******"; if (getprevcon (&user_context) == 0) { user = user_context; } SYSLOG ((LOG_ALERT, "%s is not authorized to change the password of %s", user, name)); fprintf(stderr, _("%s: %s is not authorized to change the password of %s\n"), Prog, user, name); if (NULL != user_context) { freecon (user_context); } exit (E_NOPERM); } #endif /* WITH_SELINUX */ /* * If the UID of the user does not match the current real UID, * check if I'm root. */ if (!amroot && (pw->pw_uid != getuid ())) { fprintf (stderr, _("%s: You may not view or modify password information for %s.\n"), Prog, name); SYSLOG ((LOG_WARN, "%s: can't view or modify password information for %s", Prog, name)); closelog (); exit (E_NOPERM); } if (Sflg) { print_status (pw); exit (E_SUCCESS); } #ifndef USE_PAM /* * The user name is valid, so let's get the shadow file entry. */ sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */ if (NULL == sp) { sp = pwd_to_spwd (pw); } cp = sp->sp_pwdp; /* * If there are no other flags, just change the password. */ if (!anyflag) { STRFCPY (crypt_passwd, cp); /* * See if the user is permitted to change the password. * Otherwise, go ahead and set a new password. */ check_password (pw, sp); /* * Let the user know whose password is being changed. */ if (!qflg) { printf (_("Changing password for %s\n"), name); } if (new_password (pw)) { fprintf (stderr, _("The password for %s is unchanged.\n"), name); closelog (); exit (E_NOPERM); } do_update_pwd = true; do_update_age = true; } #endif /* !USE_PAM */ /* * Before going any further, raise the ulimit to prevent colliding * into a lowered ulimit, and set the real UID to root to protect * against unexpected signals. Any keyboard signals are set to be * ignored. */ pwd_init (); #ifdef USE_PAM /* * Don't set the real UID for PAM... */ if (!anyflag) { do_pam_passwd (name, qflg, kflg); exit (E_SUCCESS); } #endif /* USE_PAM */ if (setuid (0) != 0) { fputs (_("Cannot change ID to root.\n"), stderr); SYSLOG ((LOG_ERR, "can't setuid(0)")); closelog (); exit (E_NOPERM); } if (spw_file_present ()) { update_shadow (); } else { update_noshadow (); } nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname)); closelog (); if (!qflg) { if (!anyflag) { #ifndef USE_PAM printf (_("%s: password changed.\n"), Prog); #endif /* USE_PAM */ } else { printf (_("%s: password expiry information changed.\n"), Prog); } } return E_SUCCESS; }
/* * chfn - change a user's password file information * * This command controls the GECOS field information in the password * file entry. * * The valid options are * * -f full name * -r room number * -w work phone number * -h home phone number * -o other information (*) * * (*) requires root permission to execute. */ int main (int argc, char **argv) { char *cp; /* temporary character pointer */ const struct passwd *pw; /* password file entry */ struct passwd pwent; /* modified password file entry */ char old_gecos[BUFSIZ]; /* buffer for old GECOS fields */ char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */ int flag; /* flag currently being processed */ int fflg = 0; /* -f - set full name */ int rflg = 0; /* -r - set room number */ int wflg = 0; /* -w - set work phone number */ int hflg = 0; /* -h - set home phone number */ int oflg = 0; /* -o - set other information */ char *user; #ifdef USE_PAM pam_handle_t *pamh = NULL; struct passwd *pampw; int retval; #endif sanitize_env (); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); /* * This command behaves different for root and non-root * users. */ amroot = (getuid () == 0); /* * Get the program name. The program name is used as a * prefix to most error messages. */ Prog = Basename (argv[0]); OPENLOG ("chfn"); /* * The remaining arguments will be processed one by one and executed * by this command. The name is the last argument if it does not * begin with a "-", otherwise the name is determined from the * environment and must agree with the real UID. Also, the UID will * be checked for any commands which are restricted to root only. */ while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) { switch (flag) { case 'f': if (!may_change_field ('f')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } fflg++; STRFCPY (fullnm, optarg); break; case 'h': if (!may_change_field ('h')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } hflg++; STRFCPY (homeph, optarg); break; case 'r': if (!may_change_field ('r')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } rflg++; STRFCPY (roomno, optarg); break; case 'o': if (!amroot) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } oflg++; STRFCPY (slop, optarg); break; case 'w': if (!may_change_field ('w')) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } wflg++; STRFCPY (workph, optarg); break; default: usage (); } } /* * Get the name of the user to check. It is either the command line * name, or the name getlogin() returns. */ if (optind < argc) { user = argv[optind]; pw = getpwnam (user); if (!pw) { fprintf (stderr, _("%s: unknown user %s\n"), Prog, user); exit (E_NOPERM); } } else { pw = get_my_pwent (); if (!pw) { fprintf (stderr, _ ("%s: Cannot determine your user name.\n"), Prog); exit (E_NOPERM); } user = xstrdup (pw->pw_name); } #ifdef USE_NIS /* * Now we make sure this is a LOCAL password entry for this user ... */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: cannot change user `%s' on NIS client.\n"), Prog, user); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _ ("%s: `%s' is the NIS master for this client.\n"), Prog, nis_master); } exit (E_NOPERM); } #endif /* * Non-privileged users are only allowed to change the gecos field * if the UID of the user matches the current real UID. */ if (!amroot && pw->pw_uid != getuid ()) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog (); exit (E_NOPERM); } #ifdef WITH_SELINUX /* * If the UID of the user does not match the current real UID, * check if the change is allowed by SELinux policy. */ if ((pw->pw_uid != getuid ()) && (selinux_check_passwd_access (PASSWD__CHFN) != 0)) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); closelog (); exit (E_NOPERM); } #endif #ifndef USE_PAM /* * Non-privileged users are optionally authenticated (must enter the * password of the user whose information is being changed) before * any changes can be made. Idea from util-linux chfn/chsh. * --marekm */ if (!amroot && getdef_bool ("CHFN_AUTH")) passwd_check (pw->pw_name, pw->pw_passwd, "chfn"); #else /* !USE_PAM */ retval = PAM_SUCCESS; pampw = getpwuid (getuid ()); if (pampw == NULL) { retval = PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS) { retval = pam_start ("chfn", pampw->pw_name, &conv, &pamh); } if (retval == PAM_SUCCESS) { retval = pam_authenticate (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval == PAM_SUCCESS) { retval = pam_acct_mgmt (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval != PAM_SUCCESS) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); exit (E_NOPERM); } #endif /* USE_PAM */ /* * Now get the full name. It is the first comma separated field in * the GECOS field. */ STRFCPY (old_gecos, pw->pw_gecos); cp = copy_field (old_gecos, fflg ? (char *) 0 : fullnm, slop); /* * Now get the room number. It is the next comma separated field, * if there is indeed one. */ if (cp) cp = copy_field (cp, rflg ? (char *) 0 : roomno, slop); /* * Now get the work phone number. It is the third field. */ if (cp) cp = copy_field (cp, wflg ? (char *) 0 : workph, slop); /* * Now get the home phone number. It is the fourth field. */ if (cp) cp = copy_field (cp, hflg ? (char *) 0 : homeph, slop); /* * Anything left over is "slop". */ if (cp && !oflg) { if (slop[0]) strcat (slop, ","); strcat (slop, cp); } /* * If none of the fields were changed from the command line, let the * user interactively change them. */ if (!fflg && !rflg && !wflg && !hflg && !oflg) { printf (_("Changing the user information for %s\n"), user); new_fields (); } /* * Check all of the fields for valid information */ if (valid_field (fullnm, ":,=")) { fprintf (stderr, _("%s: invalid name: \"%s\"\n"), Prog, fullnm); closelog (); exit (E_NOPERM); } if (valid_field (roomno, ":,=")) { fprintf (stderr, _("%s: invalid room number: \"%s\"\n"), Prog, roomno); closelog (); exit (E_NOPERM); } if (valid_field (workph, ":,=")) { fprintf (stderr, _("%s: invalid work phone: \"%s\"\n"), Prog, workph); closelog (); exit (E_NOPERM); } if (valid_field (homeph, ":,=")) { fprintf (stderr, _("%s: invalid home phone: \"%s\"\n"), Prog, homeph); closelog (); exit (E_NOPERM); } if (valid_field (slop, ":")) { fprintf (stderr, _("%s: \"%s\" contains illegal characters\n"), Prog, slop); closelog (); exit (E_NOPERM); } /* * Build the new GECOS field by plastering all the pieces together, * if they will fit ... */ if (strlen (fullnm) + strlen (roomno) + strlen (workph) + strlen (homeph) + strlen (slop) > (unsigned int) 80) { fprintf (stderr, _("%s: fields too long\n"), Prog); closelog (); exit (E_NOPERM); } snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s", fullnm, roomno, workph, homeph, slop[0] ? "," : "", slop); /* * Before going any further, raise the ulimit to prevent colliding * into a lowered ulimit, and set the real UID to root to protect * against unexpected signals. Any keyboard signals are set to be * ignored. */ if (setuid (0)) { fprintf (stderr, _("Cannot change ID to root.\n")); SYSLOG ((LOG_ERR, "can't setuid(0)")); closelog (); exit (E_NOPERM); } pwd_init (); /* * The passwd entry is now ready to be committed back to the * password file. Get a lock on the file and open it. */ if (!pw_lock ()) { fprintf (stderr, _ ("Cannot lock the password file; try again later.\n")); SYSLOG ((LOG_WARN, "can't lock /etc/passwd")); closelog (); exit (E_NOPERM); } if (!pw_open (O_RDWR)) { fprintf (stderr, _("Cannot open the password file.\n")); pw_unlock (); SYSLOG ((LOG_ERR, "can't open /etc/passwd")); closelog (); exit (E_NOPERM); } /* * Get the entry to update using pw_locate() - we want the real one * from /etc/passwd, not the one from getpwnam() which could contain * the shadow password if (despite the warnings) someone enables * AUTOSHADOW (or SHADOW_COMPAT in libc). --marekm */ pw = pw_locate (user); if (!pw) { pw_unlock (); fprintf (stderr, _("%s: %s not found in /etc/passwd\n"), Prog, user); exit (E_NOPERM); } /* * Make a copy of the entry, then change the gecos field. The other * fields remain unchanged. */ pwent = *pw; pwent.pw_gecos = new_gecos; /* * Update the passwd file entry. If there is a DBM file, update that * entry as well. */ if (!pw_update (&pwent)) { fprintf (stderr, _("Error updating the password entry.\n")); pw_unlock (); SYSLOG ((LOG_ERR, "error updating passwd entry")); closelog (); exit (E_NOPERM); } /* * Changes have all been made, so commit them and unlock the file. */ if (!pw_close ()) { fprintf (stderr, _("Cannot commit password file changes.\n")); pw_unlock (); SYSLOG ((LOG_ERR, "can't rewrite /etc/passwd")); closelog (); exit (E_NOPERM); } if (!pw_unlock ()) { fprintf (stderr, _("Cannot unlock the password file.\n")); SYSLOG ((LOG_ERR, "can't unlock /etc/passwd")); closelog (); exit (E_NOPERM); } SYSLOG ((LOG_INFO, "changed user `%s' information", user)); nscd_flush_cache ("passwd"); #ifdef USE_PAM if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS); #endif /* USE_PAM */ closelog (); exit (E_SUCCESS); }
/* * process_flags - perform command line argument setting * * process_flags() interprets the command line arguments and sets the * values that the user will be created with accordingly. The values * are checked for sanity. */ static void process_flags (int argc, char **argv) { const struct group *grp; const struct passwd *pwd; const struct spwd *spwd = NULL; int anyflag = 0; int arg; if (argc == 1 || argv[argc - 1][0] == '-') usage (); if (!(pwd = getpwnam (argv[argc - 1]))) { fprintf (stderr, _("%s: user %s does not exist\n"), Prog, argv[argc - 1]); exit (E_NOTFOUND); } user_name = argv[argc - 1]; user_id = pwd->pw_uid; user_gid = pwd->pw_gid; user_comment = xstrdup (pwd->pw_gecos); user_home = xstrdup (pwd->pw_dir); user_shell = xstrdup (pwd->pw_shell); #ifdef WITH_AUDIT user_newname = user_name; user_newid = user_id; user_newgid = user_gid; user_newcomment = user_comment; user_newhome = user_home; user_newshell = user_shell; #endif #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 if (is_shadow_pwd && (spwd = getspnam (user_name))) { user_expire = spwd->sp_expire; user_inactive = spwd->sp_inact; #ifdef WITH_AUDIT user_newexpire = user_expire; user_newinactive = user_inactive; #endif } { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"append", required_argument, NULL, 'a'}, {"comment", required_argument, NULL, 'c'}, {"home", required_argument, NULL, 'd'}, {"expiredate", required_argument, NULL, 'e'}, {"inactive", required_argument, NULL, 'f'}, {"gid", required_argument, NULL, 'g'}, {"groups", required_argument, NULL, 'G'}, {"help", no_argument, NULL, 'h'}, {"login", required_argument, NULL, 'l'}, {"lock", no_argument, NULL, 'L'}, {"move-home", no_argument, NULL, 'm'}, {"non-unique", no_argument, NULL, 'o'}, {"password", required_argument, NULL, 'p'}, {"shell", required_argument, NULL, 's'}, {"uid", required_argument, NULL, 'u'}, {"unlock", no_argument, NULL, 'U'}, {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, "ac:d:e:f:g:G:l:Lmop:s:u:U", long_options, NULL)) != -1) { switch (c) { case 'a': aflg++; break; case 'c': if (!VALID (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } #ifdef WITH_AUDIT user_newcomment = optarg; #else user_comment = optarg; #endif cflg++; break; case 'd': if (!VALID (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } dflg++; user_newhome = optarg; break; case 'e': if (*optarg) { #ifdef WITH_AUDIT user_newexpire = strtoday (optarg); if (user_newexpire == -1) { #else user_expire = strtoday (optarg); if (user_expire == -1) { #endif fprintf (stderr, _ ("%s: invalid date `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } #ifdef WITH_AUDIT user_newexpire *= DAY / SCALE; #else user_expire *= DAY / SCALE; #endif } else #ifdef WITH_AUDIT user_newexpire = -1; #else user_expire = -1; #endif eflg++; break; case 'f': #ifdef WITH_AUDIT user_newinactive = get_number (optarg); #else user_inactive = get_number (optarg); #endif fflg++; break; case 'g': grp = getgr_nam_gid (optarg); if (!grp) { fprintf (stderr, _("%s: unknown group %s\n"), Prog, optarg); exit (E_NOTFOUND); } user_newgid = grp->gr_gid; gflg++; break; case 'G': if (get_groups (optarg)) exit (E_NOTFOUND); Gflg++; break; case 'l': if (!check_user_name (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } /* * If the name does not really change, we mustn't * set the flag as this will cause rather serious * problems later! */ if (strcmp (user_name, optarg)) lflg++; user_newname = optarg; break; case 'L': if (Uflg || pflg) usage (); Lflg++; break; case 'm': if (!dflg) usage (); mflg++; break; case 'o': if (!uflg) usage (); oflg++; break; case 'p': if (Lflg || Uflg) usage (); user_pass = optarg; pflg++; break; case 's': if (!VALID (optarg)) { fprintf (stderr, _("%s: invalid field `%s'\n"), Prog, optarg); exit (E_BAD_ARG); } #ifdef WITH_AUDIT user_newshell = optarg; #else user_shell = optarg; #endif sflg++; break; case 'u': user_newid = get_id (optarg); uflg++; break; case 'U': if (Lflg && pflg) usage (); Uflg++; break; default: usage (); } anyflag++; } } if (anyflag == 0) { fprintf (stderr, _("%s: no flags given\n"), Prog); exit (E_USAGE); } if (!is_shadow_pwd && (eflg || fflg)) { fprintf (stderr, _ ("%s: shadow passwords required for -e and -f\n"), Prog); exit (E_USAGE); } if (optind != argc - 1) usage (); if (aflg && (!Gflg)) { fprintf (stderr, _("%s: -a flag is ONLY allowed with the -G flag\n"), Prog); usage (); exit (E_USAGE); } if (dflg && strcmp (user_home, user_newhome) == 0) dflg = mflg = 0; if (uflg && user_id == user_newid) uflg = oflg = 0; if (lflg && getpwnam (user_newname)) { fprintf (stderr, _("%s: user %s exists\n"), Prog, user_newname); exit (E_NAME_IN_USE); } if (uflg && !oflg && getpwuid (user_newid)) { fprintf (stderr, _("%s: uid %lu is not unique\n"), Prog, (unsigned long) user_newid); exit (E_UID_IN_USE); } } /* * close_files - close all of the files that were opened * * close_files() closes all of the files that were opened for this new * user. This causes any modified entries to be written out. */ static void close_files (void) { if (!pw_close ()) { fprintf (stderr, _("%s: cannot rewrite password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd && !spw_close ()) { fprintf (stderr, _("%s: cannot rewrite shadow password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd) spw_unlock (); (void) pw_unlock (); /* * Close the DBM and/or flat files */ endpwent (); endspent (); endgrent (); #ifdef SHADOWGRP endsgent (); #endif } /* * open_files - lock and open the password files * * open_files() opens the two password files. */ static void open_files (void) { if (!pw_lock ()) { fprintf (stderr, _("%s: unable to lock password file\n"), Prog); exit (E_PW_UPDATE); } if (!pw_open (O_RDWR)) { fprintf (stderr, _("%s: unable to open password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd && !spw_lock ()) { fprintf (stderr, _("%s: cannot lock shadow password file\n"), Prog); fail_exit (E_PW_UPDATE); } if (is_shadow_pwd && !spw_open (O_RDWR)) { fprintf (stderr, _("%s: cannot open shadow password file\n"), Prog); fail_exit (E_PW_UPDATE); } } /* * usr_update - create the user entries * * usr_update() creates the password file entries for this user and * will update the group entries if required. */ static void usr_update (void) { struct passwd pwent; const struct passwd *pwd; struct spwd spent; const struct spwd *spwd = NULL; /* * Locate the entry in /etc/passwd, which MUST exist. */ pwd = pw_locate (user_name); if (!pwd) { fprintf (stderr, _("%s: %s not found in /etc/passwd\n"), Prog, user_name); fail_exit (E_NOTFOUND); } pwent = *pwd; new_pwent (&pwent); /* * Locate the entry in /etc/shadow. It doesn't have to exist, and * won't be created if it doesn't. */ if (is_shadow_pwd && (spwd = spw_locate (user_name))) { spent = *spwd; new_spent (&spent); } if (lflg || uflg || gflg || cflg || dflg || sflg || pflg || Lflg || Uflg) { if (!pw_update (&pwent)) { fprintf (stderr, _("%s: error changing password entry\n"), Prog); fail_exit (E_PW_UPDATE); } if (lflg && !pw_remove (user_name)) { fprintf (stderr, _("%s: error removing password entry\n"), Prog); fail_exit (E_PW_UPDATE); } } if (spwd && (lflg || eflg || fflg || pflg || Lflg || Uflg)) { if (!spw_update (&spent)) { fprintf (stderr, _ ("%s: error adding new shadow password entry\n"), Prog); fail_exit (E_PW_UPDATE); } if (lflg && !spw_remove (user_name)) { fprintf (stderr, _ ("%s: error removing shadow password entry\n"), Prog); fail_exit (E_PW_UPDATE); } } } /* * move_home - move the user's home directory * * move_home() moves the user's home directory to a new location. The * files will be copied if the directory cannot simply be renamed. */ static void move_home (void) { struct stat sb; if (mflg && stat (user_home, &sb) == 0) { /* * Don't try to move it if it is not a directory * (but /dev/null for example). --marekm */ if (!S_ISDIR (sb.st_mode)) return; if (access (user_newhome, F_OK) == 0) { fprintf (stderr, _("%s: directory %s exists\n"), Prog, user_newhome); fail_exit (E_HOMEDIR); } else if (rename (user_home, user_newhome)) { if (errno == EXDEV) { if (mkdir (user_newhome, sb.st_mode & 0777)) { fprintf (stderr, _ ("%s: can't create %s\n"), Prog, user_newhome); } if (chown (user_newhome, sb.st_uid, sb.st_gid)) { fprintf (stderr, _("%s: can't chown %s\n"), Prog, user_newhome); rmdir (user_newhome); fail_exit (E_HOMEDIR); } if (copy_tree (user_home, user_newhome, uflg ? user_newid : -1, gflg ? user_newgid : -1) == 0) { if (remove_tree (user_home) != 0 || rmdir (user_home) != 0) fprintf (stderr, _ ("%s: warning: failed to completely remove old home directory %s"), Prog, user_home); #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "moving home directory", user_newname, user_newid, 1); #endif return; } (void) remove_tree (user_newhome); (void) rmdir (user_newhome); } fprintf (stderr, _ ("%s: cannot rename directory %s to %s\n"), Prog, user_home, user_newhome); fail_exit (E_HOMEDIR); } #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "moving home directory", user_newname, user_newid, 1); #endif } if (uflg || gflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing home directory owner", user_newname, user_newid, 1); #endif chown (dflg ? user_newhome : user_home, uflg ? user_newid : user_id, gflg ? user_newgid : user_gid); } } /* * update_files - update the lastlog and faillog files */ static void update_files (void) { struct lastlog ll; struct faillog fl; int fd; /* * Relocate the "lastlog" entries for the user. The old entry is * left alone in case the UID was shared. It doesn't hurt anything * to just leave it be. */ if ((fd = open (LASTLOG_FILE, O_RDWR)) != -1) { lseek (fd, (off_t) user_id * sizeof ll, SEEK_SET); if (read (fd, (char *) &ll, sizeof ll) == sizeof ll) { lseek (fd, (off_t) user_newid * sizeof ll, SEEK_SET); write (fd, (char *) &ll, sizeof ll); } close (fd); } /* * Relocate the "faillog" entries in the same manner. */ if ((fd = open (FAILLOG_FILE, O_RDWR)) != -1) { lseek (fd, (off_t) user_id * sizeof fl, SEEK_SET); if (read (fd, (char *) &fl, sizeof fl) == sizeof fl) { lseek (fd, (off_t) user_newid * sizeof fl, SEEK_SET); write (fd, (char *) &fl, sizeof fl); } close (fd); } } #ifndef NO_MOVE_MAILBOX /* * This is the new and improved code to carefully chown/rename the user's * mailbox. Maybe I am too paranoid but the mail spool dir sometimes * happens to be mode 1777 (this makes mail user agents work without * being setgid mail, but is NOT recommended; they all should be fixed * to use movemail). --marekm */ static void move_mailbox (void) { const char *maildir; char mailfile[1024], newmailfile[1024]; int fd; struct stat st; maildir = getdef_str ("MAIL_DIR"); #ifdef MAIL_SPOOL_DIR if (!maildir && !getdef_str ("MAIL_FILE")) maildir = MAIL_SPOOL_DIR; #endif if (!maildir) return; /* * O_NONBLOCK is to make sure open won't hang on mandatory locks. * We do fstat/fchown to make sure there are no races (someone * replacing /var/spool/mail/luser with a hard link to /etc/passwd * between stat and chown). --marekm */ snprintf (mailfile, sizeof mailfile, "%s/%s", maildir, user_name); fd = open (mailfile, O_RDONLY | O_NONBLOCK, 0); if (fd < 0) { /* no need for warnings if the mailbox doesn't exist */ if (errno != ENOENT) perror (mailfile); return; } if (fstat (fd, &st) < 0) { perror ("fstat"); close (fd); return; } if (st.st_uid != user_id) { /* better leave it alone */ fprintf (stderr, _("%s: warning: %s not owned by %s\n"), Prog, mailfile, user_name); close (fd); return; } if (uflg) { if (fchown (fd, user_newid, (gid_t) - 1) < 0) { perror (_("failed to change mailbox owner")); } #ifdef WITH_AUDIT else { audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing mail file owner", user_newname, user_newid, 1); } #endif } close (fd); if (lflg) { snprintf (newmailfile, sizeof newmailfile, "%s/%s", maildir, user_newname); if (link (mailfile, newmailfile) || unlink (mailfile)) { perror (_("failed to rename mailbox")); } #ifdef WITH_AUDIT else { audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing mail file name", user_newname, user_newid, 1); } #endif } } #endif /* * main - usermod command */ int main (int argc, char **argv) { int grp_err = 0; #ifdef USE_PAM pam_handle_t *pamh = NULL; struct passwd *pampw; int retval; #endif #ifdef WITH_AUDIT audit_help_open (); #endif /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); sys_ngroups = sysconf (_SC_NGROUPS_MAX); user_groups = malloc ((1 + sys_ngroups) * sizeof (char *)); user_groups[0] = (char *) 0; OPENLOG ("usermod"); is_shadow_pwd = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif process_flags (argc, argv); #ifdef USE_PAM retval = PAM_SUCCESS; pampw = getpwuid (getuid ()); if (pampw == NULL) { retval = PAM_USER_UNKNOWN; } if (retval == PAM_SUCCESS) { retval = pam_start ("usermod", pampw->pw_name, &conv, &pamh); } if (retval == PAM_SUCCESS) { retval = pam_authenticate (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval == PAM_SUCCESS) { retval = pam_acct_mgmt (pamh, 0); if (retval != PAM_SUCCESS) { pam_end (pamh, retval); } } if (retval != PAM_SUCCESS) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); exit (1); } #endif /* USE_PAM */ /* * Do the hard stuff - open the files, change the user entries, * change the home directory, then close and update the files. */ open_files (); usr_update (); nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); close_files (); if (Gflg || lflg) grp_err = grp_update (); if (mflg) move_home (); #ifndef NO_MOVE_MAILBOX if (lflg || uflg) move_mailbox (); #endif if (uflg) { update_files (); /* * Change the UID on all of the files owned by `user_id' to * `user_newid' in the user's home directory. */ chown_tree (dflg ? user_newhome : user_home, user_id, user_newid, user_gid, gflg ? user_newgid : user_gid); } if (grp_err) exit (E_GRP_UPDATE); #ifdef USE_PAM if (retval == PAM_SUCCESS) pam_end (pamh, PAM_SUCCESS); #endif /* USE_PAM */ exit (E_SUCCESS); /* NOT REACHED */ }
int main (int argc, char **argv) { #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef WITH_AUDIT audit_help_open (); #endif atexit (do_cleanups); /* * 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); if (argc != 2) { usage (); } group_name = argv[1]; OPENLOG ("groupdel"); #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 (1); } retval = pam_start ("groupdel", 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 (NULL != pamh) { (void) pam_end (pamh, retval); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); exit (1); } #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif { struct group *grp; /* * Start with a quick check to see if the group exists. */ grp = getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), Prog, group_name); exit (E_NOTFOUND); } group_id = grp->gr_gid; } #ifdef USE_NIS /* * Make sure this isn't a NIS group */ if (__isgrNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: group '%s' is a NIS group\n"), Prog, group_name); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif /* * Make sure this isn't the primary group of anyone. */ group_busy (group_id); /* * Do the hard stuff - open the files, delete the group entries, * then close and update the files. */ open_files (); grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *fields[8]; int nfields; char *cp; const struct passwd *pw; struct passwd newpw; int errors = 0; int line = 0; uid_t uid; gid_t gid; #ifdef USE_PAM int *lines = NULL; char **usernames = NULL; char **passwords = NULL; unsigned int nusers = 0; #endif /* USE_PAM */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* FIXME: will not work with an input file */ process_root_flag ("-R", argc, argv); OPENLOG ("newusers"); process_flags (argc, argv); check_perms (); is_shadow = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif #ifdef ENABLE_SUBIDS is_sub_uid = sub_uid_file_present () && !rflg; is_sub_gid = sub_gid_file_present () && !rflg; #endif /* ENABLE_SUBIDS */ open_files (); /* * Read each line. The line has the same format as a password file * entry, except that certain fields are not constrained to be * numerical values. If a group ID is entered which does not already * exist, an attempt is made to allocate the same group ID as the * numerical user ID. Should that fail, the next available group ID * over 100 is allocated. The pw_gid field will be updated with that * value. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { if (feof (stdin) == 0) { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } } /* * Break the string into fields and screw around with them. * There MUST be 7 colon separated fields, although the * values aren't that particular. */ for (cp = buf, nfields = 0; nfields < 7; nfields++) { fields[nfields] = cp; cp = strchr (cp, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { break; } } if (nfields != 6) { fprintf (stderr, _("%s: line %d: invalid line\n"), Prog, line); errors++; continue; } /* * First check if we have to create or update an user */ pw = pw_locate (fields[0]); /* local, no need for xgetpwnam */ if ( (NULL == pw) && (getpwnam (fields[0]) != NULL)) { fprintf (stderr, _("%s: cannot update the entry of user %s (not in the passwd database)\n"), Prog, fields[0]); errors++; continue; } if ( (NULL == pw) && (get_user_id (fields[2], &uid) != 0)) { fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); errors++; continue; } /* * Processed is the group name. A new group will be * created if the group name is non-numeric and does not * already exist. If the group name is a number (which is not * an existing GID), a group with the same name as the user * will be created, with the given GID. The given or created * group will be the primary group of the user. If * there is no named group to be a member of, the UID will * be figured out and that value will be a candidate for a * new group, if that group ID exists, a whole new group ID * will be made up. */ if ( (NULL == pw) && (add_group (fields[0], fields[3], &gid, uid) != 0)) { fprintf (stderr, _("%s: line %d: can't create group\n"), Prog, line); errors++; continue; } /* * Now we work on the user ID. It has to be specified either * as a numerical value, or left blank. If it is a numerical * value, that value will be used, otherwise the next * available user ID is computed and used. After this there * will at least be a (struct passwd) for the user. */ if ( (NULL == pw) && (add_user (fields[0], uid, gid) != 0)) { fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); errors++; continue; } /* * The password, gecos field, directory, and shell fields * all come next. */ pw = pw_locate (fields[0]); if (NULL == pw) { fprintf (stderr, _("%s: line %d: user '%s' does not exist in %s\n"), Prog, line, fields[0], pw_dbname ()); errors++; continue; } newpw = *pw; #ifdef USE_PAM /* keep the list of user/password for later update by PAM */ nusers++; lines = realloc (lines, sizeof (lines[0]) * nusers); usernames = realloc (usernames, sizeof (usernames[0]) * nusers); passwords = realloc (passwords, sizeof (passwords[0]) * nusers); lines[nusers-1] = line; usernames[nusers-1] = strdup (fields[0]); passwords[nusers-1] = strdup (fields[1]); #endif /* USE_PAM */ if (add_passwd (&newpw, fields[1]) != 0) { fprintf (stderr, _("%s: line %d: can't update password\n"), Prog, line); errors++; continue; } if ('\0' != fields[4][0]) { newpw.pw_gecos = fields[4]; } if ('\0' != fields[5][0]) { newpw.pw_dir = fields[5]; } if ('\0' != fields[6][0]) { newpw.pw_shell = fields[6]; } if ( ('\0' != fields[5][0]) && (access (newpw.pw_dir, F_OK) != 0)) { /* FIXME: should check for directory */ mode_t msk = 0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK); if (mkdir (newpw.pw_dir, msk) != 0) { fprintf (stderr, _("%s: line %d: mkdir %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); } else if (chown (newpw.pw_dir, newpw.pw_uid, newpw.pw_gid) != 0) { fprintf (stderr, _("%s: line %d: chown %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); } } /* * Update the password entry with the new changes made. */ if (pw_update (&newpw) == 0) { fprintf (stderr, _("%s: line %d: can't update entry\n"), Prog, line); errors++; continue; } #ifdef ENABLE_SUBIDS /* * Add subordinate uids if the user does not have them. */ if (is_sub_uid && !sub_uid_assigned(fields[0])) { uid_t sub_uid_start = 0; unsigned long sub_uid_count = 0; if (find_new_sub_uids(fields[0], &sub_uid_start, &sub_uid_count) == 0) { if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0) { fprintf (stderr, _("%s: failed to prepare new %s entry\n"), Prog, sub_uid_dbname ()); } } else { fprintf (stderr, _("%s: can't find subordinate user range\n"), Prog); errors++; } } /* * Add subordinate gids if the user does not have them. */ if (is_sub_gid && !sub_gid_assigned(fields[0])) { gid_t sub_gid_start = 0; unsigned long sub_gid_count = 0; if (find_new_sub_gids(fields[0], &sub_gid_start, &sub_gid_count) == 0) { if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) { fprintf (stderr, _("%s: failed to prepare new %s entry\n"), Prog, sub_uid_dbname ()); } } else { fprintf (stderr, _("%s: can't find subordinate group range\n"), Prog); errors++; } } #endif /* ENABLE_SUBIDS */ } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the password file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. */ if (0 != errors) { fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); fail_exit (EXIT_FAILURE); } close_files (); nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); #ifdef USE_PAM unsigned int i; /* Now update the passwords using PAM */ for (i = 0; i < nusers; i++) { if (do_pam_passwd_non_interactive ("newusers", usernames[i], passwords[i]) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, lines[i], usernames[i]); errors++; } } #endif /* USE_PAM */ return ((0 == errors) ? EXIT_SUCCESS : EXIT_FAILURE); }
int main (int argc, char **argv) { const struct passwd *pw; struct passwd pwent; const struct spwd *spwd; if (1 != argc) { (void) fputs (_("Usage: pwunconv\n"), stderr); } Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); OPENLOG ("pwunconv"); #ifdef WITH_TCB if (getdef_bool("USE_TCB")) { fprintf(stderr, _("%s: can't work with tcb enabled\n"), Prog); exit(1); } #endif /* WITH_TCB */ if (!spw_file_present ()) { /* shadow not installed, do nothing */ exit (0); } if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); fail_exit (5); } pw_locked = true; if (pw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); fail_exit (1); } if (spw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, spw_dbname ()); fail_exit (5); } spw_locked = true; if (spw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, spw_dbname ()); fail_exit (1); } pw_rewind (); while ((pw = pw_next ()) != NULL) { spwd = spw_locate (pw->pw_name); if (NULL == spwd) { continue; } pwent = *pw; /* * Update password if non-shadow is "x". */ if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) { pwent.pw_passwd = spwd->sp_pwdp; } /* * Password aging works differently in the two different * systems. With shadow password files you apparently must * have some aging information. The maxweeks or minweeks * may not map exactly. In pwconv we set max == 10000, * which is about 30 years. Here we have to undo that * kludge. So, if maxdays == 10000, no aging information is * put into the new file. Otherwise, the days are converted * to weeks and so on. */ if (pw_update (&pwent) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), pwent.pw_name); fail_exit (3); } } if (spw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ())); fail_exit (3); } if (pw_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); fail_exit (3); } if (unlink (SHADOW) != 0) { fprintf (stderr, _("%s: cannot delete %s\n"), Prog, SHADOW); SYSLOG ((LOG_ERR, "cannot delete %s", SHADOW)); fail_exit (3); } if (spw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); /* continue */ } if (pw_unlock () == 0) { fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); /* continue */ } nscd_flush_cache ("passwd"); return 0; }
/* * grpck - verify group file integrity */ int main (int argc, char **argv) { int arg; int errors = 0; int deleted = 0; int i; struct commonio_entry *gre, *tgre; struct group *grp; int sort_mode = 0; #ifdef SHADOWGRP struct commonio_entry *sge, *tsge; struct sgrp *sgr; int is_shadow = 0; #endif /* * 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 ("grpck"); /* * Parse the command line arguments */ while ((arg = getopt (argc, argv, "qrs")) != EOF) { switch (arg) { case 'q': /* quiet - ignored for now */ break; case 'r': read_only = 1; break; case 's': sort_mode = 1; break; default: usage (); } } if (sort_mode && read_only) { fprintf (stderr, _("%s: -s and -r are incompatibile\n"), Prog); exit (E_USAGE); } /* * Make certain we have the right number of arguments */ #ifdef SHADOWGRP if (optind != argc && optind + 1 != argc && optind + 2 != argc) #else if (optind != argc && optind + 1 != argc) #endif usage (); /* * If there are two left over filenames, use those as the group and * group password filenames. */ if (optind != argc) { grp_file = argv[optind]; gr_name (grp_file); } #ifdef SHADOWGRP if (optind + 2 == argc) { sgr_file = argv[optind + 1]; sgr_name (sgr_file); is_shadow = 1; } else if (optind == argc) is_shadow = sgr_file_present (); #endif /* * Lock the files if we aren't in "read-only" mode */ if (!read_only) { if (!gr_lock ()) { fprintf (stderr, _("%s: cannot lock file %s\n"), Prog, grp_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot lock %s", grp_file)); closelog (); exit (E_CANT_LOCK); } #ifdef SHADOWGRP if (is_shadow && !sgr_lock ()) { fprintf (stderr, _("%s: cannot lock file %s\n"), Prog, sgr_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot lock %s", sgr_file)); closelog (); exit (E_CANT_LOCK); } #endif } /* * Open the files. Use O_RDONLY if we are in read_only mode, * O_RDWR otherwise. */ if (!gr_open (read_only ? O_RDONLY : O_RDWR)) { fprintf (stderr, _("%s: cannot open file %s\n"), Prog, grp_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot open %s", grp_file)); closelog (); exit (E_CANT_OPEN); } #ifdef SHADOWGRP if (is_shadow && !sgr_open (read_only ? O_RDONLY : O_RDWR)) { fprintf (stderr, _("%s: cannot open file %s\n"), Prog, sgr_file); if (optind == argc) SYSLOG ((LOG_WARN, "cannot open %s", sgr_file)); closelog (); exit (E_CANT_OPEN); } #endif if (sort_mode) { gr_sort (); #ifdef SHADOWGRP if (is_shadow) sgr_sort (); #endif goto write_and_bye; } /* * Loop through the entire group file. */ for (gre = __gr_get_head (); gre; gre = gre->next) { /* * Skip all NIS entries. */ if (gre->line[0] == '+' || gre->line[0] == '-') continue; /* * Start with the entries that are completely corrupt. They * have no (struct group) entry because they couldn't be * parsed properly. */ if (!gre->eptr) { /* * Tell the user this entire line is bogus and ask * them to delete it. */ printf (_("invalid group file entry\n")); printf (_("delete line `%s'? "), gre->line); errors++; /* * prompt the user to delete the entry or not */ if (!yes_or_no ()) continue; /* * All group file deletions wind up here. This code * removes the current entry from the linked list. * When done, it skips back to the top of the loop * to try out the next list element. */ delete_gr: SYSLOG ((LOG_INFO, "delete group line `%s'", gre->line)); deleted++; __gr_del_entry (gre); continue; } /* * Group structure is good, start using it. */ grp = gre->eptr; /* * Make sure this entry has a unique name. */ for (tgre = __gr_get_head (); tgre; tgre = tgre->next) { const struct group *ent = tgre->eptr; /* * Don't check this entry */ if (tgre == gre) continue; /* * Don't check invalid entries. */ if (!ent) continue; if (strcmp (grp->gr_name, ent->gr_name) != 0) continue; /* * Tell the user this entry is a duplicate of * another and ask them to delete it. */ printf (_("duplicate group entry\n")); printf (_("delete line `%s'? "), gre->line); errors++; /* * prompt the user to delete the entry or not */ if (yes_or_no ()) goto delete_gr; } /* * Check for invalid group names. --marekm */ if (!check_group_name (grp->gr_name)) { errors++; printf (_("invalid group name `%s'\n"), grp->gr_name); } /* * Workaround for a NYS libc 5.3.12 bug on RedHat 4.2 - * groups with no members are returned as groups with one * member "", causing grpck to fail. --marekm */ if (grp->gr_mem[0] && !grp->gr_mem[1] && *(grp->gr_mem[0]) == '\0') grp->gr_mem[0] = (char *) 0; /* * Make sure each member exists */ for (i = 0; grp->gr_mem[i]; i++) { if (getpwnam (grp->gr_mem[i])) continue; /* * Can't find this user. Remove them * from the list. */ errors++; printf (_("group %s: no user %s\n"), grp->gr_name, grp->gr_mem[i]); printf (_("delete member `%s'? "), grp->gr_mem[i]); if (!yes_or_no ()) continue; SYSLOG ((LOG_INFO, "delete member `%s' group `%s'", grp->gr_mem[i], grp->gr_name)); deleted++; delete_member (grp->gr_mem, grp->gr_mem[i]); gre->changed = 1; __gr_set_changed (); } } #ifdef SHADOWGRP if (!is_shadow) goto shadow_done; /* * Loop through the entire shadow group file. */ for (sge = __sgr_get_head (); sge; sge = sge->next) { /* * Start with the entries that are completely corrupt. They * have no (struct sgrp) entry because they couldn't be * parsed properly. */ if (!sge->eptr) { /* * Tell the user this entire line is bogus and ask * them to delete it. */ printf (_("invalid shadow group file entry\n")); printf (_("delete line `%s'? "), sge->line); errors++; /* * prompt the user to delete the entry or not */ if (!yes_or_no ()) continue; /* * All shadow group file deletions wind up here. * This code removes the current entry from the * linked list. When done, it skips back to the top * of the loop to try out the next list element. */ delete_sg: SYSLOG ((LOG_INFO, "delete shadow line `%s'", sge->line)); deleted++; __sgr_del_entry (sge); continue; } /* * Shadow group structure is good, start using it. */ sgr = sge->eptr; /* * Make sure this entry has a unique name. */ for (tsge = __sgr_get_head (); tsge; tsge = tsge->next) { const struct sgrp *ent = tsge->eptr; /* * Don't check this entry */ if (tsge == sge) continue; /* * Don't check invalid entries. */ if (!ent) continue; if (strcmp (sgr->sg_name, ent->sg_name) != 0) continue; /* * Tell the user this entry is a duplicate of * another and ask them to delete it. */ printf (_("duplicate shadow group entry\n")); printf (_("delete line `%s'? "), sge->line); errors++; /* * prompt the user to delete the entry or not */ if (yes_or_no ()) goto delete_sg; } /* * Make sure this entry exists in the /etc/group file. */ if (!gr_locate (sgr->sg_name)) { printf (_("no matching group file entry\n")); printf (_("delete line `%s'? "), sge->line); errors++; if (yes_or_no ()) goto delete_sg; } /* * Make sure each administrator exists */ for (i = 0; sgr->sg_adm[i]; i++) { if (getpwnam (sgr->sg_adm[i])) continue; /* * Can't find this user. Remove them * from the list. */ errors++; printf (_ ("shadow group %s: no administrative user %s\n"), sgr->sg_name, sgr->sg_adm[i]); printf (_("delete administrative member `%s'? "), sgr->sg_adm[i]); if (!yes_or_no ()) continue; SYSLOG ((LOG_INFO, "delete admin `%s' from shadow group `%s'", sgr->sg_adm[i], sgr->sg_name)); deleted++; delete_member (sgr->sg_adm, sgr->sg_adm[i]); sge->changed = 1; __sgr_set_changed (); } /* * Make sure each member exists */ for (i = 0; sgr->sg_mem[i]; i++) { if (getpwnam (sgr->sg_mem[i])) continue; /* * Can't find this user. Remove them from the list. */ errors++; printf (_("shadow group %s: no user %s\n"), sgr->sg_name, sgr->sg_mem[i]); printf (_("delete member `%s'? "), sgr->sg_mem[i]); if (!yes_or_no ()) continue; SYSLOG ((LOG_INFO, "delete member `%s' from shadow group `%s'", sgr->sg_mem[i], sgr->sg_name)); deleted++; delete_member (sgr->sg_mem, sgr->sg_mem[i]); sge->changed = 1; __sgr_set_changed (); } } shadow_done: #endif /* SHADOWGRP */ /* * All done. If there were no deletions we can just abandon any * changes to the files. */ if (deleted) { write_and_bye: if (!gr_close ()) { fprintf (stderr, _("%s: cannot update file %s\n"), Prog, grp_file); exit (E_CANT_UPDATE); } #ifdef SHADOWGRP if (is_shadow && !sgr_close ()) { fprintf (stderr, _("%s: cannot update file %s\n"), Prog, sgr_file); exit (E_CANT_UPDATE); } #endif } /* * Don't be anti-social - unlock the files when you're done. */ #ifdef SHADOWGRP if (is_shadow) sgr_unlock (); #endif (void) gr_unlock (); nscd_flush_cache ("group"); /* * Tell the user what we did and exit. */ if (errors) printf (deleted ? _("%s: the files have been updated\n") : _("%s: no changes\n"), Prog); exit (errors ? E_BAD_ENTRY : E_OKAY); }