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); }
static void get_group (struct group *gr) #endif { struct group const*tmpgr = NULL; #ifdef SHADOWGRP struct sgrp const*tmpsg = NULL; #endif if (gr_open (O_RDONLY) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ())); exit (E_NOPERM); } tmpgr = gr_locate (group); if (NULL == tmpgr) { fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), Prog, group, gr_dbname ()); exit (E_BAD_ARG); } *gr = *tmpgr; gr->gr_name = xstrdup (tmpgr->gr_name); gr->gr_passwd = xstrdup (tmpgr->gr_passwd); gr->gr_mem = dup_list (tmpgr->gr_mem); if (gr_close () == 0) { fprintf (stderr, _("%s: failure while closing read-only %s\n"), Prog, gr_dbname ()); SYSLOG ((LOG_ERR, "failure while closing read-only %s", gr_dbname ())); exit (E_NOPERM); } #ifdef SHADOWGRP if (is_shadowgrp) { if (sgr_open (O_RDONLY) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ()); SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ())); exit (E_NOPERM); } tmpsg = sgr_locate (group); if (NULL != tmpsg) { *sg = *tmpsg; sg->sg_name = xstrdup (tmpsg->sg_name); sg->sg_passwd = xstrdup (tmpsg->sg_passwd); sg->sg_mem = dup_list (tmpsg->sg_mem); sg->sg_adm = dup_list (tmpsg->sg_adm); } else { sg->sg_name = xstrdup (group); sg->sg_passwd = gr->gr_passwd; gr->gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ sg->sg_mem = dup_list (gr->gr_mem); sg->sg_adm = (char **) xmalloc (sizeof (char *) * 2); #ifdef FIRST_MEMBER_IS_ADMIN if (sg->sg_mem[0]) { sg->sg_adm[0] = xstrdup (sg->sg_mem[0]); sg->sg_adm[1] = NULL; } else #endif { sg->sg_adm[0] = NULL; } } if (sgr_close () == 0) { fprintf (stderr, _("%s: failure while closing read-only %s\n"), Prog, sgr_dbname ()); SYSLOG ((LOG_ERR, "failure while closing read-only %s", sgr_dbname ())); exit (E_NOPERM); } } #endif /* SHADOWGRP */ }
/* * add_group - create a new group or add a user to an existing group */ static int add_group (const char *name, const char *gid, gid_t *ngid, uid_t uid) { const struct group *grp; struct group grent; char *members[1]; #ifdef SHADOWGRP const struct sgrp *sg; #endif /* * Start by seeing if the named group already exists. This will be * very easy to deal with if it does. */ grp = getgrnam (gid); if (NULL == grp) { grp = gr_locate (gid); } if (NULL != grp) { /* The user will use this ID for her primary group */ *ngid = grp->gr_gid; /* Don't check gshadow */ return 0; } if (isdigit (gid[0])) { /* * The GID is a number, which means either this is a brand * new group, or an existing group. */ if (get_gid (gid, &grent.gr_gid) == 0) { fprintf (stderr, _("%s: invalid group ID '%s'\n"), Prog, gid); return -1; } /* Look in both the system database (getgrgid) and in the * internal database (gr_locate_gid), which may contain * uncommitted changes */ if ( (getgrgid ((gid_t) grent.gr_gid) != NULL) || (gr_locate_gid ((gid_t) grent.gr_gid) != NULL)) { /* The user will use this ID for her * primary group */ *ngid = (gid_t) grent.gr_gid; return 0; } /* Do not create groups with GID == (gid_t)-1 */ if (grent.gr_gid == (gid_t)-1) { fprintf (stderr, _("%s: invalid group ID '%s'\n"), Prog, gid); return -1; } } else { /* The gid parameter can be "" or a name which is not * already the name of an existing group. * In both cases, figure out what group ID can be used. */ if (find_new_gid(rflg, &grent.gr_gid, &uid) < 0) { return -1; } } /* * Now I have all of the fields required to create the new group. */ if (('\0' != gid[0]) && (!isdigit (gid[0]))) { grent.gr_name = xstrdup (gid); } else { grent.gr_name = xstrdup (name); /* FIXME: check if the group exists */ } /* Check if this is a valid group name */ if (!is_valid_group_name (grent.gr_name)) { fprintf (stderr, _("%s: invalid group name '%s'\n"), Prog, grent.gr_name); if (grent.gr_name) free (grent.gr_name); return -1; } grent.gr_passwd = "*"; /* XXX warning: const */ members[0] = NULL; grent.gr_mem = members; *ngid = grent.gr_gid; #ifdef SHADOWGRP if (is_shadow_grp) { sg = sgr_locate (grent.gr_name); if (NULL != sg) { fprintf (stderr, _("%s: group '%s' is a shadow group, but does not exist in /etc/group\n"), Prog, grent.gr_name); return -1; } } #endif #ifdef SHADOWGRP if (is_shadow_grp) { struct sgrp sgrent; char *admins[1]; sgrent.sg_name = grent.gr_name; sgrent.sg_passwd = "*"; /* XXX warning: const */ grent.gr_passwd = "x"; /* XXX warning: const */ admins[0] = NULL; sgrent.sg_adm = admins; sgrent.sg_mem = members; if (sgr_update (&sgrent) == 0) { return -1; } } #endif if (gr_update (&grent) == 0) { return -1; } return 0; }
/* * check_sgr_file - check the content of the shadowed group file (gshadow) */ static void check_sgr_file (int *errors, bool *changed) { struct group *grp; struct commonio_entry *sge, *tsge; struct sgrp *sgr; /* * Loop through the entire shadow group file. */ for (sge = __sgr_get_head (); NULL != 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 (NULL == sge->eptr) { /* * Tell the user this entire line is bogus and ask * them to delete it. */ (void) puts (_("invalid shadow group file entry")); printf (_("delete line '%s'? "), sge->line); *errors += 1; /* * prompt the user to delete the entry or not */ if (!yes_or_no (read_only)) { 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)); *changed = true; __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 (); NULL != 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 (NULL == 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. */ (void) puts (_("duplicate shadow group entry")); printf (_("delete line '%s'? "), sge->line); *errors += 1; /* * prompt the user to delete the entry or not */ if (yes_or_no (read_only)) { goto delete_sg; } } /* * Make sure this entry exists in the /etc/group file. */ grp = (struct group *) gr_locate (sgr->sg_name); if (grp == NULL) { printf (_("no matching group file entry in %s\n"), grp_file); printf (_("delete line '%s'? "), sge->line); *errors += 1; if (yes_or_no (read_only)) { goto delete_sg; } } else { /** * Verify that the all members defined in /etc/gshadow are also * present in /etc/group. */ compare_members_lists (sgr->sg_name, sgr->sg_mem, grp->gr_mem, sgr_file, grp_file); } /* * Make sure each administrator exists */ if (check_members (sgr->sg_name, sgr->sg_adm, _("shadow group %s: no administrative user %s\n"), _("delete administrative member '%s'? "), "delete admin '%s' from shadow group '%s'", errors) == 1) { *changed = true; sge->changed = true; __sgr_set_changed (); } /* * Make sure each member exists */ if (check_members (sgr->sg_name, sgr->sg_mem, _("shadow group %s: no user %s\n"), _("delete member '%s'? "), "delete member '%s' from shadow group '%s'", errors) == 1) { *changed = true; sge->changed = true; __sgr_set_changed (); } } }
/* * grp_update - update group file entries * * grp_update() updates the new records in the memory databases. */ static void grp_update (void) { struct group grp; const struct group *ogrp; #ifdef SHADOWGRP struct sgrp sgrp; const struct sgrp *osgrp = NULL; #endif /* SHADOWGRP */ /* * Get the current settings for this group. */ ogrp = gr_locate (group_name); if (!ogrp) { fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), Prog, group_name, gr_dbname ()); exit (E_GRP_UPDATE); } grp = *ogrp; new_grent (&grp); #ifdef SHADOWGRP if ( is_shadow_grp && (pflg || nflg)) { osgrp = sgr_locate (group_name); if (NULL != osgrp) { sgrp = *osgrp; new_sgent (&sgrp); if (pflg) { grp.gr_passwd = SHADOW_PASSWD_STRING; } } } #endif /* SHADOWGRP */ if (gflg) { update_primary_groups (ogrp->gr_gid, group_newid); } /* * Write out the new group file entry. */ if (gr_update (&grp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grp.gr_name); exit (E_GRP_UPDATE); } if (nflg && (gr_remove (group_name) == 0)) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, grp.gr_name, gr_dbname ()); exit (E_GRP_UPDATE); } #ifdef SHADOWGRP /* * Make sure there was a shadow entry to begin with. */ if ( (NULL != osgrp) && (pflg || nflg)) { /* * Write out the new shadow group entries as well. */ if (sgr_update (&sgrp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, sgr_dbname (), sgrp.sg_name); exit (E_GRP_UPDATE); } if (nflg && (sgr_remove (group_name) == 0)) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, group_name, sgr_dbname ()); exit (E_GRP_UPDATE); } } #endif /* SHADOWGRP */ }
/* * grp_update - update group file entries * * grp_update() updates the new records in the memory databases. */ static void grp_update (void) { struct group grp; const struct group *ogrp; #ifdef SHADOWGRP struct sgrp sgrp; const struct sgrp *osgrp = NULL; #endif /* SHADOWGRP */ /* * Get the current settings for this group. */ ogrp = gr_locate (group_name); if (NULL == ogrp) { fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), Prog, group_name, gr_dbname ()); exit (E_GRP_UPDATE); } grp = *ogrp; new_grent (&grp); #ifdef SHADOWGRP if ( is_shadow_grp && (pflg || nflg)) { osgrp = sgr_locate (group_name); if (NULL != osgrp) { sgrp = *osgrp; new_sgent (&sgrp); } else if ( pflg && (strcmp (grp.gr_passwd, SHADOW_PASSWD_STRING) == 0)) { static char *empty = NULL; /* If there is a gshadow file with no entries for * the group, but the group file indicates a * shadowed password, we force the creation of a * gshadow entry when a new password is requested. */ memset (&sgrp, 0, sizeof sgrp); sgrp.sg_name = xstrdup (grp.gr_name); sgrp.sg_passwd = xstrdup (grp.gr_passwd); sgrp.sg_adm = ∅ sgrp.sg_mem = dup_list (grp.gr_mem); new_sgent (&sgrp); osgrp = &sgrp; /* entry needs to be committed */ } } #endif /* SHADOWGRP */ if (gflg) { update_primary_groups (ogrp->gr_gid, group_newid); } /* * Write out the new group file entry. */ if (gr_update (&grp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grp.gr_name); exit (E_GRP_UPDATE); } if (nflg && (gr_remove (group_name) == 0)) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, grp.gr_name, gr_dbname ()); exit (E_GRP_UPDATE); } #ifdef SHADOWGRP /* * Make sure there was a shadow entry to begin with. */ if (NULL != osgrp) { /* * Write out the new shadow group entries as well. */ if (sgr_update (&sgrp) == 0) { fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, sgr_dbname (), sgrp.sg_name); exit (E_GRP_UPDATE); } if (nflg && (sgr_remove (group_name) == 0)) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, group_name, sgr_dbname ()); exit (E_GRP_UPDATE); } } #endif /* SHADOWGRP */ }
/* * remove_usergroup - delete the user's group if it is a usergroup * * An usergroup is removed if * + it has the same name as the user * + it is the primary group of the user * + it has no other members * + it is not the primary group of any other user */ static void remove_usergroup (void) { const struct group *grp; const struct passwd *pwd = NULL; grp = gr_locate (user_name); if (NULL == grp) { /* This user has no usergroup. */ return; } if (grp->gr_gid != user_gid) { fprintf (stderr, _("%s: group %s not removed because it is not the primary group of user %s.\n"), Prog, grp->gr_name, user_name); return; } if (NULL != grp->gr_mem[0]) { /* The usergroup has other members. */ fprintf (stderr, _("%s: group %s not removed because it has other members.\n"), Prog, grp->gr_name); return; } if (!fflg) { /* * Scan the passwd file to check if this group is still * used as a primary group. */ setpwent (); while ((pwd = getpwent ()) != NULL) { if (strcmp (pwd->pw_name, user_name) == 0) { continue; } if (pwd->pw_gid == grp->gr_gid) { fprintf (stderr, _("%s: group %s is the primary group of another user and is not removed.\n"), Prog, grp->gr_name); break; } } endpwent (); } if (NULL == pwd) { /* * We can remove this group, it is not the primary * group of any remaining user. */ if (gr_remove (user_name) == 0) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, user_name, gr_dbname ()); fail_exit (E_GRP_UPDATE); } #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_GROUP, Prog, "deleting group", user_name, AUDIT_NO_ID, SHADOW_AUDIT_SUCCESS); #endif /* WITH_AUDIT */ SYSLOG ((LOG_INFO, "removed group '%s' owned by '%s'\n", user_name, user_name)); #ifdef SHADOWGRP if (sgr_locate (user_name) != NULL) { if (sgr_remove (user_name) == 0) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, user_name, sgr_dbname ()); fail_exit (E_GRP_UPDATE); } #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_GROUP, Prog, "deleting shadow group", user_name, AUDIT_NO_ID, SHADOW_AUDIT_SUCCESS); #endif /* WITH_AUDIT */ SYSLOG ((LOG_INFO, "removed shadow group '%s' owned by '%s'\n", user_name, user_name)); } #endif /* SHADOWGRP */ } }
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; }
/* * 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); }