X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fgrpck.c;h=ca4d72e18d9c7d56a1b34106888f3bd562b7967f;hb=4e75bb57bb1b8d1f29c376354d43183c9fbc2884;hp=58465fc7793983908ef400c5dd64ae2d6fa2244b;hpb=8e167d28afd6177c5720e6d84bf6982fee94ef40;p=shadow diff --git a/src/grpck.c b/src/grpck.c index 58465fc7..ca4d72e1 100644 --- a/src/grpck.c +++ b/src/grpck.c @@ -1,5 +1,9 @@ /* - * Copyright 1992 - 1994, Julianne Frances Haugh + * Copyright (c) 1992 - 1994, Julianne Frances Haugh + * Copyright (c) 1996 - 2000, Marek Michałkiewicz + * Copyright (c) 2001 , Michał Moskal + * Copyright (c) 2001 - 2006, Tomasz Kłoczko + * Copyright (c) 2007 - 2009, Nicolas François * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -10,49 +14,46 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Julianne F. Haugh nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * 3. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -#include "rcsid.h" -RCSID (PKG_VER "$Id: grpck.c,v 1.23 2005/04/06 04:26:06 kloczek Exp $") -#include +#ident "$Id$" + #include #include -#include "prototypes.h" -#include "defines.h" -#include "chkname.h" #include +#include +#include "chkname.h" #include "commonio.h" +#include "defines.h" #include "groupio.h" -extern void __gr_del_entry (const struct commonio_entry *); -extern struct commonio_entry *__gr_get_head (void); +#include "nscd.h" +#include "prototypes.h" #ifdef SHADOWGRP #include "sgroupio.h" -extern void __sgr_del_entry (const struct commonio_entry *); -extern struct commonio_entry *__sgr_get_head (void); #endif /* * Exit codes */ - +/*@-exitarg@*/ #define E_OKAY 0 #define E_USAGE 1 #define E_BAD_ENTRY 2 @@ -61,26 +62,78 @@ extern struct commonio_entry *__sgr_get_head (void); #define E_CANT_UPDATE 5 /* - * Local variables + * Global variables */ +char *Prog; -static char *Prog; static const char *grp_file = GROUP_FILE; +static bool use_system_grp_file = true; #ifdef SHADOWGRP static const char *sgr_file = SGROUP_FILE; +static bool use_system_sgr_file = true; +static bool is_shadow = false; +static bool sgr_locked = false; #endif -static int read_only = 0; +static bool gr_locked = false; +/* Options */ +static bool read_only = false; +static bool sort_mode = false; /* local function prototypes */ +static void fail_exit (int status); static void usage (void); -static int yes_or_no (void); static void delete_member (char **, const char *); +static void process_flags (int argc, char **argv); +static void open_files (void); +static void close_files (bool changed); +static int check_members (const char *groupname, + char **members, + const char *fmt_info, + const char *fmt_prompt, + const char *fmt_syslog, + int *errors); +static void check_grp_file (int *errors, bool *changed); +#ifdef SHADOWGRP +static void compare_members_lists (const char *groupname, + char **members, + char **other_members, + const char *file, + const char *other_file); +static void check_sgr_file (int *errors, bool *changed); +#endif /* - * usage - print syntax message and exit + * fail_exit - exit with an error code after unlocking files */ +static void fail_exit (int status) +{ + if (gr_locked) { + 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 */ + } + } +#ifdef SHADOWGRP + if (sgr_locked) { + 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 */ + } + } +#endif + + closelog (); + + exit (status); +} + +/* + * usage - print syntax message and exit + */ static void usage (void) { #ifdef SHADOWGRP @@ -91,96 +144,50 @@ static void usage (void) exit (E_USAGE); } -/* - * yes_or_no - get answer to question from the user - */ - -static int yes_or_no (void) -{ - char buf[80]; - - /* - * In read-only mode all questions are answered "no". - */ - - if (read_only) { - puts (_("No")); - return 0; - } - - /* - * Get a line and see what the first character is. - */ - - if (fgets (buf, sizeof buf, stdin)) - return buf[0] == 'y' || buf[0] == 'Y'; - - return 0; -} - /* * delete_member - delete an entry in a list of members + * + * It only deletes the first entry with the given name. */ - static void delete_member (char **list, const char *member) { int i; - for (i = 0; list[i]; i++) - if (list[i] == member) + for (i = 0; list[i]; i++) { + if (list[i] == member) { break; + } + } - if (list[i]) - for (; list[i]; i++) + if (list[i]) { + for (; list[i]; i++) { list[i] = list[i + 1]; + } + } } /* - * grpck - verify group file integrity + * process_flags - parse the command line options + * + * It will not return if an error is encountered. */ - -int main (int argc, char **argv) +static void process_flags (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; + read_only = true; break; case 's': - sort_mode = 1; + sort_mode = true; break; default: usage (); @@ -195,53 +202,64 @@ int main (int argc, char **argv) /* * Make certain we have the right number of arguments */ - #ifdef SHADOWGRP - if (optind != argc && optind + 1 != argc && optind + 2 != argc) + if ((argc < optind) || (argc > (optind + 2))) #else - if (optind != argc && optind + 1 != argc) + if ((argc < optind) || (argc > (optind + 1))) #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); + gr_setdbname (grp_file); + use_system_grp_file = false; } #ifdef SHADOWGRP - if (optind + 2 == argc) { + if ((optind + 2) == argc) { sgr_file = argv[optind + 1]; - sgr_name (sgr_file); - is_shadow = 1; - } else if (optind == argc) + sgr_setdbname (sgr_file); + is_shadow = true; + use_system_sgr_file = false; + } else if (optind == argc) { is_shadow = sgr_file_present (); + } #endif +} +/* + * open_files - open the shadow database + * + * In read-only mode, the databases are not locked and are opened + * only for reading. + */ +static void open_files (void) +{ /* * 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); + if (gr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, grp_file); + fail_exit (E_CANT_LOCK); } + gr_locked = true; #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); + if (is_shadow) { + if (sgr_lock () == 0) { + fprintf (stderr, + _("%s: cannot lock %s; try again later.\n"), + Prog, sgr_file); + fail_exit (E_CANT_LOCK); + } + sgr_locked = true; } #endif } @@ -250,70 +268,215 @@ int main (int argc, char **argv) * 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) + if (gr_open (read_only ? O_RDONLY : O_RDWR) == 0) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, + grp_file); + if (use_system_grp_file) { SYSLOG ((LOG_WARN, "cannot open %s", grp_file)); - closelog (); - exit (E_CANT_OPEN); + } + fail_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) + if (is_shadow && (sgr_open (read_only ? O_RDONLY : O_RDWR) == 0)) { + fprintf (stderr, _("%s: cannot open %s\n"), Prog, + sgr_file); + if (use_system_sgr_file) { SYSLOG ((LOG_WARN, "cannot open %s", sgr_file)); - closelog (); - exit (E_CANT_OPEN); + } + fail_exit (E_CANT_OPEN); } #endif +} - if (sort_mode) { - gr_sort (); +/* + * close_files - close and unlock the group/gshadow databases + * + * If changed is not set, the databases are not closed, and no + * changes are committed in the databases. The databases are + * unlocked anyway. + */ +static void close_files (bool changed) +{ + /* + * All done. If there were no change we can just abandon any + * changes to the files. + */ + if (changed) { + if (gr_close () == 0) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), + Prog, grp_file); + fail_exit (E_CANT_UPDATE); + } #ifdef SHADOWGRP - if (is_shadow) - sgr_sort (); + if (is_shadow && (sgr_close () == 0)) { + fprintf (stderr, _("%s: failure while writing changes to %s\n"), + Prog, sgr_file); + fail_exit (E_CANT_UPDATE); + } #endif - goto write_and_bye; } /* - * Loop through the entire group file. + * Don't be anti-social - unlock the files when you're done. + */ +#ifdef SHADOWGRP + if (sgr_locked) { + 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 */ + } + sgr_locked = false; + } +#endif + if (gr_locked) { + 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 */ + } + gr_locked = false; + } +} + +/* + * check_members - check that every members of a group exist + * + * If an error is detected, *errors is incremented. + * + * The user will be prompted for the removal of the non-existent + * user. + * + * If any changes are performed, the return value will be 1, + * otherwise check_members() returns 0. + * + * fmt_info, fmt_prompt, and fmt_syslog are used for logging. + * * fmt_info must contain two string flags (%s): for the group's + * name and the missing member. + * * fmt_prompt must contain one string flags (%s): the missing + * member. + * * fmt_syslog must contain two string flags (%s): for the + * group's name and the missing member. + */ +static int check_members (const char *groupname, + char **members, + const char *fmt_info, + const char *fmt_prompt, + const char *fmt_syslog, + int *errors) +{ + int i; + int members_changed = 0; + + /* + * Make sure each member exists */ + for (i = 0; NULL != members[i]; i++) { + /* local, no need for xgetpwnam */ + if (getpwnam (members[i]) != NULL) { + continue; + } + /* + * Can't find this user. Remove them + * from the list. + */ + *errors += 1; + printf (fmt_info, groupname, members[i]); + printf (fmt_prompt, members[i]); + + if (!yes_or_no (read_only)) { + continue; + } + + SYSLOG ((LOG_INFO, fmt_syslog, members[i], groupname)); + members_changed = 1; + delete_member (members, members[i]); + + /* Rewind in case of removal */ + i--; + } + + return members_changed; +} + +#ifdef SHADOWGRP +/* + * compare_members_lists - make sure the list of members is contained in + * another list. + * + * compare_members_lists() checks that all the members of members are + * also in other_members. + * file and other_file are used for logging. + * + * TODO: No changes are performed on the lists. + */ +static void compare_members_lists (const char *groupname, + char **members, + char **other_members, + const char *file, + const char *other_file) +{ + char **pmem, **other_pmem; + + for (pmem = members; NULL != *pmem; pmem++) { + for (other_pmem = other_members; NULL != *other_pmem; other_pmem++) { + if (strcmp (*pmem, *other_pmem) == 0) { + break; + } + } + if (*other_pmem == NULL) { + printf + ("'%s' is a member of the '%s' group in %s but not in %s\n", + *pmem, groupname, file, other_file); + } + } +} +#endif /* SHADOWGRP */ - for (gre = __gr_get_head (); gre; gre = gre->next) { +/* + * check_grp_file - check the content of the group file + */ +static void check_grp_file (int *errors, bool *changed) +{ + struct commonio_entry *gre, *tgre; + struct group *grp; +#ifdef SHADOWGRP + struct sgrp *sgr; +#endif + + /* + * Loop through the entire group file. + */ + for (gre = __gr_get_head (); NULL != gre; gre = gre->next) { /* * Skip all NIS entries. */ - if (gre->line[0] == '+' || gre->line[0] == '-') + 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) { + if (NULL == 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++; + (void) puts (_("invalid group file entry")); + printf (_("delete line '%s'? "), gre->line); + *errors += 1; /* * prompt the user to delete the entry or not */ - - if (!yes_or_no ()) + if (!yes_or_no (read_only)) { continue; + } /* * All group file deletions wind up here. This code @@ -321,11 +484,10 @@ int main (int argc, char **argv) * 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++; + SYSLOG ((LOG_INFO, "delete group line '%s'", + gre->line)); + *changed = true; __gr_del_entry (gre); continue; @@ -334,57 +496,63 @@ int main (int argc, char **argv) /* * 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) { + for (tgre = __gr_get_head (); NULL != tgre; tgre = tgre->next) { const struct group *ent = tgre->eptr; /* * Don't check this entry */ - - if (tgre == gre) + if (tgre == gre) { continue; + } /* * Don't check invalid entries. */ - - if (!ent) + if (NULL == ent) { continue; + } - if (strcmp (grp->gr_name, ent->gr_name) != 0) + 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. */ - - puts (_("duplicate group entry\n")); - printf (_("delete line `%s'? "), gre->line); - errors++; + (void) puts (_("duplicate group entry")); + printf (_("delete line '%s'? "), gre->line); + *errors += 1; /* * prompt the user to delete the entry or not */ - - if (yes_or_no ()) + if (yes_or_no (read_only)) { 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); + if (!is_valid_group_name (grp->gr_name)) { + *errors += 1; + printf (_("invalid group name '%s'\n"), grp->gr_name); + } + + /* + * Check for invalid group ID. + */ + if (grp->gr_gid == (gid_t)-1) { + printf (_("invalid group ID '%lu'\n"), (long unsigned int)grp->gr_gid); + *errors += 1; } /* @@ -392,73 +560,116 @@ int main (int argc, char **argv) * groups with no members are returned as groups with one * member "", causing grpck to fail. --marekm */ + if ( (NULL != grp->gr_mem[0]) + && (NULL == grp->gr_mem[1]) + && ('\0' == grp->gr_mem[0][0])) { + grp->gr_mem[0] = NULL; + } - if (grp->gr_mem[0] && !grp->gr_mem[1] - && *(grp->gr_mem[0]) == '\0') - grp->gr_mem[0] = (char *) 0; + if (check_members (grp->gr_name, grp->gr_mem, + _("group %s: no user %s\n"), + _("delete member '%s'? "), + "delete member '%s' from group '%s'", + errors) == 1) { + *changed = true; + gre->changed = true; + __gr_set_changed (); + } +#ifdef SHADOWGRP /* - * Make sure each member exists + * Make sure this entry exists in the /etc/gshadow file. */ - 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 (); + if (is_shadow) { + sgr = (struct sgrp *) sgr_locate (grp->gr_name); + if (sgr == NULL) { + printf (_("no matching group file entry in %s\n"), + sgr_file); + printf (_("add group '%s' in %s? "), + grp->gr_name, sgr_file); + *errors += 1; + if (yes_or_no (read_only)) { + struct sgrp sg; + struct group gr; + static char *empty = NULL; + + sg.sg_name = grp->gr_name; + sg.sg_passwd = grp->gr_passwd; + sg.sg_adm = ∅ + sg.sg_mem = grp->gr_mem; + SYSLOG ((LOG_INFO, + "add group '%s' to '%s'", + grp->gr_name, sgr_file)); + *changed = true; + + if (sgr_update (&sg) == 0) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, sgr_dbname (), sg.sg_name); + fail_exit (E_CANT_UPDATE); + } + /* remove password from /etc/group */ + gr = *grp; + gr.gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */ + if (gr_update (&gr) == 0) { + fprintf (stderr, + _("%s: failed to prepare the new %s entry '%s'\n"), + Prog, gr_dbname (), gr.gr_name); + fail_exit (E_CANT_UPDATE); + } + } + } else { + /** + * Verify that all the members defined in /etc/group are also + * present in /etc/gshadow. + */ + compare_members_lists (grp->gr_name, + grp->gr_mem, sgr->sg_mem, + grp_file, sgr_file); + } } +#endif + } +} -#ifdef SHADOWGRP - if (!is_shadow) - goto shadow_done; +#ifdef SHADOWGRP +/* + * 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 (); sge; sge = sge->next) { + 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 (!sge->eptr) { + if (NULL == 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++; + (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 ()) + if (!yes_or_no (read_only)) { continue; + } /* * All shadow group file deletions wind up here. @@ -466,11 +677,10 @@ int main (int argc, char **argv) * 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++; + SYSLOG ((LOG_INFO, "delete shadow line '%s'", + sge->line)); + *changed = true; __sgr_del_entry (sge); continue; @@ -479,166 +689,157 @@ int main (int argc, char **argv) /* * 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) { + for (tsge = __sgr_get_head (); NULL != tsge; tsge = tsge->next) { const struct sgrp *ent = tsge->eptr; /* * Don't check this entry */ - - if (tsge == sge) + if (tsge == sge) { continue; + } /* * Don't check invalid entries. */ - - if (!ent) + if (NULL == ent) { continue; + } - if (strcmp (sgr->sg_name, ent->sg_name) != 0) + 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. */ - - puts (_("duplicate shadow group entry\n")); - printf (_("delete line `%s'? "), sge->line); - errors++; + (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 ()) + if (yes_or_no (read_only)) { goto delete_sg; + } } /* * Make sure this entry exists in the /etc/group file. */ - - if (!gr_locate (sgr->sg_name)) { - puts (_("no matching group file entry\n")); - printf (_("delete line `%s'? "), sge->line); - errors++; - if (yes_or_no ()) + 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 */ - - 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; + 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 */ - - 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; + 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 (); } } - - shadow_done: +} #endif /* SHADOWGRP */ +/* + * grpck - verify group file integrity + */ +int main (int argc, char **argv) +{ + int errors = 0; + bool changed = false; + /* - * All done. If there were no deletions we can just abandon any - * changes to the files. + * 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 ("grpck"); - if (deleted) { - write_and_bye: - if (!gr_close ()) { - fprintf (stderr, _("%s: cannot update file %s\n"), - Prog, grp_file); - exit (E_CANT_UPDATE); + /* 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 && !sgr_close ()) { - fprintf (stderr, _("%s: cannot update file %s\n"), - Prog, sgr_file); - exit (E_CANT_UPDATE); + if (is_shadow) { + check_sgr_file (&errors, &changed); } #endif } - /* - * Don't be anti-social - unlock the files when you're done. - */ + /* Commit the change in the database if needed */ + close_files (changed); -#ifdef SHADOWGRP - if (is_shadow) - sgr_unlock (); -#endif - (void) gr_unlock (); + 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); + } + } - if (errors) - printf (deleted ? - _("%s: the files have been updated\n") : - _("%s: no changes\n"), Prog); - - exit (errors ? E_BAD_ENTRY : E_OKAY); + return ((0 != errors) ? E_BAD_ENTRY : E_OKAY); } +