]> granicus.if.org Git - shadow/blob - src/grpck.c
remove unused fn commonio_next
[shadow] / src / grpck.c
1 /*
2  * Copyright (c) 1992 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2001       , Michał Moskal
5  * Copyright (c) 2001 - 2006, Tomasz Kłoczko
6  * Copyright (c) 2007 - 2011, Nicolas François
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the copyright holders or contributors may not be used to
18  *    endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
25  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <config.h>
35
36 #ident "$Id$"
37
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #include <stdio.h>
42 #include <getopt.h>
43 #include "chkname.h"
44 #include "commonio.h"
45 #include "defines.h"
46 #include "groupio.h"
47 #include "nscd.h"
48 #include "sssd.h"
49 #include "prototypes.h"
50
51 #ifdef SHADOWGRP
52 #include "sgroupio.h"
53 #endif
54
55 /*
56  * Exit codes
57  */
58 /*@-exitarg@*/
59 #define E_OKAY          0
60 #define E_SUCCESS       0
61 #define E_USAGE         1
62 #define E_BAD_ENTRY     2
63 #define E_CANT_OPEN     3
64 #define E_CANT_LOCK     4
65 #define E_CANT_UPDATE   5
66
67 /*
68  * Global variables
69  */
70 const char *Prog;
71
72 static const char *grp_file = GROUP_FILE;
73 static bool use_system_grp_file = true;
74
75 #ifdef  SHADOWGRP
76 static const char *sgr_file = SGROUP_FILE;
77 static bool use_system_sgr_file = true;
78 static bool is_shadow = false;
79 static bool sgr_locked = false;
80 #endif
81 static bool gr_locked = false;
82 /* Options */
83 static bool read_only = false;
84 static bool sort_mode = false;
85
86 /* local function prototypes */
87 static void fail_exit (int status);
88 static /*@noreturn@*/void usage (int status);
89 static void delete_member (char **, const char *);
90 static void process_flags (int argc, char **argv);
91 static void open_files (void);
92 static void close_files (bool changed);
93 static int check_members (const char *groupname,
94                           char **members,
95                           const char *fmt_info,
96                           const char *fmt_prompt,
97                           const char *fmt_syslog,
98                           int *errors);
99 static void check_grp_file (int *errors, bool *changed);
100 #ifdef SHADOWGRP
101 static void compare_members_lists (const char *groupname,
102                                    char **members,
103                                    char **other_members,
104                                    const char *file,
105                                    const char *other_file);
106 static void check_sgr_file (int *errors, bool *changed);
107 #endif
108
109 /*
110  * fail_exit - exit with an error code after unlocking files
111  */
112 static void fail_exit (int status)
113 {
114         if (gr_locked) {
115                 if (gr_unlock () == 0) {
116                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
117                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
118                         /* continue */
119                 }
120         }
121
122 #ifdef  SHADOWGRP
123         if (sgr_locked) {
124                 if (sgr_unlock () == 0) {
125                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
126                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
127                         /* continue */
128                 }
129         }
130 #endif
131
132         closelog ();
133
134         exit (status);
135 }
136
137 /*
138  * usage - print syntax message and exit
139  */
140 static /*@noreturn@*/void usage (int status)
141 {
142         FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
143 #ifdef  SHADOWGRP
144         (void) fprintf (usageout,
145                         _("Usage: %s [options] [group [gshadow]]\n"
146                           "\n"
147                           "Options:\n"),
148                         Prog);
149 #else                           /* !SHADOWGRP */
150         (void) fprintf (usageout,
151                         _("Usage: %s [options] [group]\n"
152                           "\n"
153                           "Options:\n"),
154                         Prog);
155 #endif                          /* !SHADOWGRP */
156         (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
157         (void) fputs (_("  -r, --read-only               display errors and warnings\n"
158                         "                                but do not change files\n"), usageout);
159         (void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
160         (void) fputs (_("  -s, --sort                    sort entries by UID\n"), usageout);
161         (void) fputs ("\n", usageout);
162         exit (status);
163 }
164
165 /*
166  * delete_member - delete an entry in a list of members
167  *
168  * It only deletes the first entry with the given name.
169  * The member is defined by its address, no string comparison are
170  * performed.
171  */
172 static void delete_member (char **list, const char *member)
173 {
174         int i;
175
176         for (i = 0; NULL != list[i]; i++) {
177                 if (list[i] == member) {
178                         break;
179                 }
180         }
181
182         for (; NULL != list[i]; i++) {
183                 list[i] = list[i + 1];
184         }
185 }
186
187 /*
188  * process_flags - parse the command line options
189  *
190  *      It will not return if an error is encountered.
191  */
192 static void process_flags (int argc, char **argv)
193 {
194         int c;
195         static struct option long_options[] = {
196                 {"help",      no_argument,       NULL, 'h'},
197                 {"quiet",     no_argument,       NULL, 'q'},
198                 {"read-only", no_argument,       NULL, 'r'},
199                 {"root",      required_argument, NULL, 'R'},
200                 {"sort",      no_argument,       NULL, 's'},
201                 {NULL, 0, NULL, '\0'}
202         };
203
204         /*
205          * Parse the command line arguments
206          */
207         while ((c = getopt_long (argc, argv, "hqrR:s",
208                                  long_options, NULL)) != -1) {
209                 switch (c) {
210                 case 'h':
211                         usage (E_SUCCESS);
212                         /*@notreached@*/break;
213                 case 'q':
214                         /* quiet - ignored for now */
215                         break;
216                 case 'r':
217                         read_only = true;
218                         break;
219                 case 'R': /* no-op, handled in process_root_flag () */
220                         break;
221                 case 's':
222                         sort_mode = true;
223                         break;
224                 default:
225                         usage (E_USAGE);
226                 }
227         }
228
229         if (sort_mode && read_only) {
230                 fprintf (stderr, _("%s: -s and -r are incompatible\n"), Prog);
231                 exit (E_USAGE);
232         }
233
234         /*
235          * Make certain we have the right number of arguments
236          */
237 #ifdef  SHADOWGRP
238         if (argc > (optind + 2))
239 #else
240         if (argc > (optind + 1))
241 #endif
242         {
243                 usage (E_USAGE);
244         }
245
246         /*
247          * If there are two left over filenames, use those as the group and
248          * group password filenames.
249          */
250         if (optind != argc) {
251                 grp_file = argv[optind];
252                 gr_setdbname (grp_file);
253                 use_system_grp_file = false;
254         }
255 #ifdef  SHADOWGRP
256         if ((optind + 2) == argc) {
257                 sgr_file = argv[optind + 1];
258                 sgr_setdbname (sgr_file);
259                 is_shadow = true;
260                 use_system_sgr_file = false;
261         } else if (optind == argc) {
262                 is_shadow = sgr_file_present ();
263         }
264 #endif
265 }
266
267 /*
268  * open_files - open the shadow database
269  *
270  *      In read-only mode, the databases are not locked and are opened
271  *      only for reading.
272  */
273 static void open_files (void)
274 {
275         /*
276          * Lock the files if we aren't in "read-only" mode
277          */
278         if (!read_only) {
279                 if (gr_lock () == 0) {
280                         fprintf (stderr,
281                                  _("%s: cannot lock %s; try again later.\n"),
282                                  Prog, grp_file);
283                         fail_exit (E_CANT_LOCK);
284                 }
285                 gr_locked = true;
286 #ifdef  SHADOWGRP
287                 if (is_shadow) {
288                         if (sgr_lock () == 0) {
289                                 fprintf (stderr,
290                                          _("%s: cannot lock %s; try again later.\n"),
291                                          Prog, sgr_file);
292                                 fail_exit (E_CANT_LOCK);
293                         }
294                         sgr_locked = true;
295                 }
296 #endif
297         }
298
299         /*
300          * Open the files. Use O_RDONLY if we are in read_only mode,
301          * O_RDWR otherwise.
302          */
303         if (gr_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0) {
304                 fprintf (stderr, _("%s: cannot open %s\n"), Prog,
305                          grp_file);
306                 if (use_system_grp_file) {
307                         SYSLOG ((LOG_WARN, "cannot open %s", grp_file));
308                 }
309                 fail_exit (E_CANT_OPEN);
310         }
311 #ifdef  SHADOWGRP
312         if (is_shadow && (sgr_open (read_only ? O_RDONLY : O_CREAT | O_RDWR) == 0)) {
313                 fprintf (stderr, _("%s: cannot open %s\n"), Prog,
314                          sgr_file);
315                 if (use_system_sgr_file) {
316                         SYSLOG ((LOG_WARN, "cannot open %s", sgr_file));
317                 }
318                 fail_exit (E_CANT_OPEN);
319         }
320 #endif
321 }
322
323 /*
324  * close_files - close and unlock the group/gshadow databases
325  *
326  *      If changed is not set, the databases are not closed, and no
327  *      changes are committed in the databases. The databases are
328  *      unlocked anyway.
329  */
330 static void close_files (bool changed)
331 {
332         /*
333          * All done. If there were no change we can just abandon any
334          * changes to the files.
335          */
336         if (changed) {
337                 if (gr_close () == 0) {
338                         fprintf (stderr, _("%s: failure while writing changes to %s\n"),
339                                  Prog, grp_file);
340                         fail_exit (E_CANT_UPDATE);
341                 }
342 #ifdef  SHADOWGRP
343                 if (is_shadow && (sgr_close () == 0)) {
344                         fprintf (stderr, _("%s: failure while writing changes to %s\n"),
345                                  Prog, sgr_file);
346                         fail_exit (E_CANT_UPDATE);
347                 }
348 #endif
349         }
350
351         /*
352          * Don't be anti-social - unlock the files when you're done.
353          */
354 #ifdef  SHADOWGRP
355         if (sgr_locked) {
356                 if (sgr_unlock () == 0) {
357                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
358                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
359                         /* continue */
360                 }
361                 sgr_locked = false;
362         }
363 #endif
364         if (gr_locked) {
365                 if (gr_unlock () == 0) {
366                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
367                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
368                         /* continue */
369                 }
370                 gr_locked = false;
371         }
372 }
373
374 /*
375  * check_members - check that every members of a group exist
376  *
377  *      If an error is detected, *errors is incremented.
378  *
379  *      The user will be prompted for the removal of the non-existent
380  *      user.
381  *
382  *      If any changes are performed, the return value will be 1,
383  *      otherwise check_members() returns 0.
384  *
385  *      fmt_info, fmt_prompt, and fmt_syslog are used for logging.
386  *        * fmt_info must contain two string flags (%s): for the group's
387  *          name and the missing member.
388  *        * fmt_prompt must contain one string flags (%s): the missing
389  *          member.
390  *        * fmt_syslog must contain two string flags (%s): for the
391  *          group's name and the missing member.
392  */
393 static int check_members (const char *groupname,
394                           char **members,
395                           const char *fmt_info,
396                           const char *fmt_prompt,
397                           const char *fmt_syslog,
398                           int *errors)
399 {
400         int i;
401         int members_changed = 0;
402
403         /*
404          * Make sure each member exists
405          */
406         for (i = 0; NULL != members[i]; i++) {
407                 /* local, no need for xgetpwnam */
408                 if (getpwnam (members[i]) != NULL) {
409                         continue;
410                 }
411                 /*
412                  * Can't find this user. Remove them
413                  * from the list.
414                  */
415                 *errors += 1;
416                 printf (fmt_info, groupname, members[i]);
417                 printf (fmt_prompt, members[i]);
418
419                 if (!yes_or_no (read_only)) {
420                         continue;
421                 }
422
423                 SYSLOG ((LOG_INFO, fmt_syslog, members[i], groupname));
424                 members_changed = 1;
425                 delete_member (members, members[i]);
426
427                 /* Rewind in case of removal */
428                 i--;
429         }
430
431         return members_changed;
432 }
433
434 #ifdef SHADOWGRP
435 /*
436  * compare_members_lists - make sure the list of members is contained in
437  *                         another list.
438  *
439  *      compare_members_lists() checks that all the members of members are
440  *      also in other_members.
441  *      file and other_file are used for logging.
442  *
443  *      TODO: No changes are performed on the lists.
444  */
445 static void compare_members_lists (const char *groupname,
446                                    char **members,
447                                    char **other_members,
448                                    const char *file,
449                                    const char *other_file)
450 {
451         char **pmem, **other_pmem;
452
453         for (pmem = members; NULL != *pmem; pmem++) {
454                 for (other_pmem = other_members; NULL != *other_pmem; other_pmem++) {
455                         if (strcmp (*pmem, *other_pmem) == 0) {
456                                 break;
457                         }
458                 }
459                 if (*other_pmem == NULL) {
460                         printf
461                             ("'%s' is a member of the '%s' group in %s but not in %s\n",
462                              *pmem, groupname, file, other_file);
463                 }
464         }
465 }
466 #endif                          /* SHADOWGRP */
467
468 /*
469  * check_grp_file - check the content of the group file
470  */
471 static void check_grp_file (int *errors, bool *changed)
472 {
473         struct commonio_entry *gre, *tgre;
474         struct group *grp;
475 #ifdef SHADOWGRP
476         struct sgrp *sgr;
477 #endif
478
479         /*
480          * Loop through the entire group file.
481          */
482         for (gre = __gr_get_head (); NULL != gre; gre = gre->next) {
483                 /*
484                  * Skip all NIS entries.
485                  */
486
487                 if ((gre->line[0] == '+') || (gre->line[0] == '-')) {
488                         continue;
489                 }
490
491                 /*
492                  * Start with the entries that are completely corrupt. They
493                  * have no (struct group) entry because they couldn't be
494                  * parsed properly.
495                  */
496                 if (NULL == gre->eptr) {
497
498                         /*
499                          * Tell the user this entire line is bogus and ask
500                          * them to delete it.
501                          */
502                         (void) puts (_("invalid group file entry"));
503                         printf (_("delete line '%s'? "), gre->line);
504                         *errors += 1;
505
506                         /*
507                          * prompt the user to delete the entry or not
508                          */
509                         if (!yes_or_no (read_only)) {
510                                 continue;
511                         }
512
513                         /*
514                          * All group file deletions wind up here. This code
515                          * removes the current entry from the linked list.
516                          * When done, it skips back to the top of the loop
517                          * to try out the next list element.
518                          */
519                       delete_gr:
520                         SYSLOG ((LOG_INFO, "delete group line '%s'",
521                                  gre->line));
522                         *changed = true;
523
524                         __gr_del_entry (gre);
525                         continue;
526                 }
527
528                 /*
529                  * Group structure is good, start using it.
530                  */
531                 grp = gre->eptr;
532
533                 /*
534                  * Make sure this entry has a unique name.
535                  */
536                 for (tgre = __gr_get_head (); NULL != tgre; tgre = tgre->next) {
537
538                         const struct group *ent = tgre->eptr;
539
540                         /*
541                          * Don't check this entry
542                          */
543                         if (tgre == gre) {
544                                 continue;
545                         }
546
547                         /*
548                          * Don't check invalid entries.
549                          */
550                         if (NULL == ent) {
551                                 continue;
552                         }
553
554                         if (strcmp (grp->gr_name, ent->gr_name) != 0) {
555                                 continue;
556                         }
557
558                         /*
559                          * Tell the user this entry is a duplicate of
560                          * another and ask them to delete it.
561                          */
562                         (void) puts (_("duplicate group entry"));
563                         printf (_("delete line '%s'? "), gre->line);
564                         *errors += 1;
565
566                         /*
567                          * prompt the user to delete the entry or not
568                          */
569                         if (yes_or_no (read_only)) {
570                                 goto delete_gr;
571                         }
572                 }
573
574                 /*
575                  * Check for invalid group names.  --marekm
576                  */
577                 if (!is_valid_group_name (grp->gr_name)) {
578                         *errors += 1;
579                         printf (_("invalid group name '%s'\n"), grp->gr_name);
580                 }
581
582                 /*
583                  * Check for invalid group ID.
584                  */
585                 if (grp->gr_gid == (gid_t)-1) {
586                         printf (_("invalid group ID '%lu'\n"), (long unsigned int)grp->gr_gid);
587                         *errors += 1;
588                 }
589
590                 /*
591                  * Workaround for a NYS libc 5.3.12 bug on RedHat 4.2 -
592                  * groups with no members are returned as groups with one
593                  * member "", causing grpck to fail.  --marekm
594                  */
595                 if (   (NULL != grp->gr_mem[0])
596                     && (NULL == grp->gr_mem[1])
597                     && ('\0' == grp->gr_mem[0][0])) {
598                         grp->gr_mem[0] = NULL;
599                 }
600
601                 if (check_members (grp->gr_name, grp->gr_mem,
602                                    _("group %s: no user %s\n"),
603                                    _("delete member '%s'? "),
604                                    "delete member '%s' from group '%s'",
605                                    errors) == 1) {
606                         *changed = true;
607                         gre->changed = true;
608                         __gr_set_changed ();
609                 }
610
611 #ifdef  SHADOWGRP
612                 /*
613                  * Make sure this entry exists in the /etc/gshadow file.
614                  */
615
616                 if (is_shadow) {
617                         sgr = (struct sgrp *) sgr_locate (grp->gr_name);
618                         if (sgr == NULL) {
619                                 printf (_("no matching group file entry in %s\n"),
620                                         sgr_file);
621                                 printf (_("add group '%s' in %s? "),
622                                         grp->gr_name, sgr_file);
623                                 *errors += 1;
624                                 if (yes_or_no (read_only)) {
625                                         struct sgrp sg;
626                                         struct group gr;
627                                         static char *empty = NULL;
628
629                                         sg.sg_name = grp->gr_name;
630                                         sg.sg_passwd = grp->gr_passwd;
631                                         sg.sg_adm = &empty;
632                                         sg.sg_mem = grp->gr_mem;
633                                         SYSLOG ((LOG_INFO,
634                                                  "add group '%s' to '%s'",
635                                                  grp->gr_name, sgr_file));
636                                         *changed = true;
637
638                                         if (sgr_update (&sg) == 0) {
639                                                 fprintf (stderr,
640                                                          _("%s: failed to prepare the new %s entry '%s'\n"),
641                                                          Prog, sgr_dbname (), sg.sg_name);
642                                                 fail_exit (E_CANT_UPDATE);
643                                         }
644                                         /* remove password from /etc/group */
645                                         gr = *grp;
646                                         gr.gr_passwd = SHADOW_PASSWD_STRING;    /* XXX warning: const */
647                                         if (gr_update (&gr) == 0) {
648                                                 fprintf (stderr,
649                                                          _("%s: failed to prepare the new %s entry '%s'\n"),
650                                                          Prog, gr_dbname (), gr.gr_name);
651                                                 fail_exit (E_CANT_UPDATE);
652                                         }
653                                 }
654                         } else {
655                                 /**
656                                  * Verify that all the members defined in /etc/group are also
657                                  * present in /etc/gshadow.
658                                  */
659                                 compare_members_lists (grp->gr_name,
660                                                        grp->gr_mem, sgr->sg_mem,
661                                                        grp_file, sgr_file);
662
663                                 /* The group entry has a gshadow counterpart.
664                                  * Make sure no passwords are in group.
665                                  */
666                                 if (strcmp (grp->gr_passwd, SHADOW_PASSWD_STRING) != 0) {
667                                         printf (_("group %s has an entry in %s, but its password field in %s is not set to 'x'\n"),
668                                                 grp->gr_name, sgr_file, grp_file);
669                                         *errors += 1;
670                                 }
671                         }
672                 }
673 #endif
674
675         }
676 }
677
678 #ifdef SHADOWGRP
679 /*
680  * check_sgr_file - check the content of the shadowed group file (gshadow)
681  */
682 static void check_sgr_file (int *errors, bool *changed)
683 {
684         struct group *grp;
685         struct commonio_entry *sge, *tsge;
686         struct sgrp *sgr;
687
688         /*
689          * Loop through the entire shadow group file.
690          */
691         for (sge = __sgr_get_head (); NULL != sge; sge = sge->next) {
692
693                 /*
694                  * Start with the entries that are completely corrupt. They
695                  * have no (struct sgrp) entry because they couldn't be
696                  * parsed properly.
697                  */
698                 if (NULL == sge->eptr) {
699
700                         /*
701                          * Tell the user this entire line is bogus and ask
702                          * them to delete it.
703                          */
704                         (void) puts (_("invalid shadow group file entry"));
705                         printf (_("delete line '%s'? "), sge->line);
706                         *errors += 1;
707
708                         /*
709                          * prompt the user to delete the entry or not
710                          */
711                         if (!yes_or_no (read_only)) {
712                                 continue;
713                         }
714
715                         /*
716                          * All shadow group file deletions wind up here. 
717                          * This code removes the current entry from the
718                          * linked list. When done, it skips back to the top
719                          * of the loop to try out the next list element.
720                          */
721                       delete_sg:
722                         SYSLOG ((LOG_INFO, "delete shadow line '%s'",
723                                  sge->line));
724                         *changed = true;
725
726                         __sgr_del_entry (sge);
727                         continue;
728                 }
729
730                 /*
731                  * Shadow group structure is good, start using it.
732                  */
733                 sgr = sge->eptr;
734
735                 /*
736                  * Make sure this entry has a unique name.
737                  */
738                 for (tsge = __sgr_get_head (); NULL != tsge; tsge = tsge->next) {
739
740                         const struct sgrp *ent = tsge->eptr;
741
742                         /*
743                          * Don't check this entry
744                          */
745                         if (tsge == sge) {
746                                 continue;
747                         }
748
749                         /*
750                          * Don't check invalid entries.
751                          */
752                         if (NULL == ent) {
753                                 continue;
754                         }
755
756                         if (strcmp (sgr->sg_name, ent->sg_name) != 0) {
757                                 continue;
758                         }
759
760                         /*
761                          * Tell the user this entry is a duplicate of
762                          * another and ask them to delete it.
763                          */
764                         (void) puts (_("duplicate shadow group entry"));
765                         printf (_("delete line '%s'? "), sge->line);
766                         *errors += 1;
767
768                         /*
769                          * prompt the user to delete the entry or not
770                          */
771                         if (yes_or_no (read_only)) {
772                                 goto delete_sg;
773                         }
774                 }
775
776                 /*
777                  * Make sure this entry exists in the /etc/group file.
778                  */
779                 grp = (struct group *) gr_locate (sgr->sg_name);
780                 if (grp == NULL) {
781                         printf (_("no matching group file entry in %s\n"),
782                                 grp_file);
783                         printf (_("delete line '%s'? "), sge->line);
784                         *errors += 1;
785                         if (yes_or_no (read_only)) {
786                                 goto delete_sg;
787                         }
788                 } else {
789                         /**
790                          * Verify that the all members defined in /etc/gshadow are also
791                          * present in /etc/group.
792                          */
793                         compare_members_lists (sgr->sg_name,
794                                                sgr->sg_mem, grp->gr_mem,
795                                                sgr_file, grp_file);
796                 }
797
798                 /*
799                  * Make sure each administrator exists
800                  */
801                 if (check_members (sgr->sg_name, sgr->sg_adm,
802                                    _("shadow group %s: no administrative user %s\n"),
803                                    _("delete administrative member '%s'? "),
804                                    "delete admin '%s' from shadow group '%s'",
805                                    errors) == 1) {
806                         *changed = true;
807                         sge->changed = true;
808                         __sgr_set_changed ();
809                 }
810
811                 /*
812                  * Make sure each member exists
813                  */
814                 if (check_members (sgr->sg_name, sgr->sg_mem,
815                                    _("shadow group %s: no user %s\n"),
816                                    _("delete member '%s'? "),
817                                    "delete member '%s' from shadow group '%s'",
818                                    errors) == 1) {
819                         *changed = true;
820                         sge->changed = true;
821                         __sgr_set_changed ();
822                 }
823         }
824 }
825 #endif                          /* SHADOWGRP */
826
827 /*
828  * grpck - verify group file integrity
829  */
830 int main (int argc, char **argv)
831 {
832         int errors = 0;
833         bool changed = false;
834
835         /*
836          * Get my name so that I can use it to report errors.
837          */
838         Prog = Basename (argv[0]);
839
840         (void) setlocale (LC_ALL, "");
841         (void) bindtextdomain (PACKAGE, LOCALEDIR);
842         (void) textdomain (PACKAGE);
843
844         process_root_flag ("-R", argc, argv);
845
846         OPENLOG ("grpck");
847
848         /* Parse the command line arguments */
849         process_flags (argc, argv);
850
851         open_files ();
852
853         if (sort_mode) {
854                 gr_sort ();
855 #ifdef  SHADOWGRP
856                 if (is_shadow) {
857                         sgr_sort ();
858                 }
859                 changed = true;
860 #endif
861         } else {
862                 check_grp_file (&errors, &changed);
863 #ifdef  SHADOWGRP
864                 if (is_shadow) {
865                         check_sgr_file (&errors, &changed);
866                 }
867 #endif
868         }
869
870         /* Commit the change in the database if needed */
871         close_files (changed);
872
873         if (!read_only) {
874                 nscd_flush_cache ("group");
875                 sssd_flush_cache (SSSD_DB_GROUP);
876         }
877
878         /*
879          * Tell the user what we did and exit.
880          */
881         if (0 != errors) {
882                 if (changed) {
883                         printf (_("%s: the files have been updated\n"), Prog);
884                 } else {
885                         printf (_("%s: no changes\n"), Prog);
886                 }
887         }
888
889         return ((0 != errors) ? E_BAD_ENTRY : E_OKAY);
890 }
891