]> granicus.if.org Git - shadow/blob - src/groupmod.c
* src/newusers.c: Fix typo.
[shadow] / src / groupmod.c
1 /*
2  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2011, Nicolas François
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. The name of the copyright holders or contributors may not be used to
17  *    endorse or promote products derived from this software without
18  *    specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <config.h>
34
35 #ident "$Id$"
36
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <grp.h>
41 #include <stdio.h>
42 #include <sys/types.h>
43 #ifdef ACCT_TOOLS_SETUID
44 #ifdef USE_PAM
45 #include "pam_defs.h"
46 #include <pwd.h>
47 #endif                          /* USE_PAM */
48 #endif                          /* ACCT_TOOLS_SETUID */
49 #include "chkname.h"
50 #include "defines.h"
51 #include "groupio.h"
52 #include "pwio.h"
53 #include "nscd.h"
54 #include "prototypes.h"
55 #ifdef  SHADOWGRP
56 #include "sgroupio.h"
57 #endif
58 /*
59  * exit status values
60  */
61 /*@-exitarg@*/
62 #define E_SUCCESS       0       /* success */
63 #define E_USAGE         2       /* invalid command syntax */
64 #define E_BAD_ARG       3       /* invalid argument to option */
65 #define E_GID_IN_USE    4       /* gid already in use (and no -o) */
66 #define E_NOTFOUND      6       /* specified group doesn't exist */
67 #define E_NAME_IN_USE   9       /* group name already in use */
68 #define E_GRP_UPDATE    10      /* can't update group file */
69 /*
70  * Global variables
71  */
72 const char *Prog;
73
74 #ifdef  SHADOWGRP
75 static bool is_shadow_grp;
76 #endif                          /* SHADOWGRP */
77 static char *group_name;
78 static char *group_newname;
79 static char *group_passwd;
80 static gid_t group_id;
81 static gid_t group_newid;
82
83 static struct cleanup_info_mod info_passwd;
84 static struct cleanup_info_mod info_group;
85 #ifdef  SHADOWGRP
86 static struct cleanup_info_mod info_gshadow;
87 #endif
88
89 static bool
90     oflg = false,               /* permit non-unique group ID to be specified with -g */
91     gflg = false,               /* new ID value for the group */
92     nflg = false,               /* a new name has been specified for the group */
93     pflg = false;               /* new encrypted password */
94
95 /* local function prototypes */
96 static void usage (int status);
97 static void new_grent (struct group *);
98
99 #ifdef SHADOWGRP
100 static void new_sgent (struct sgrp *);
101 #endif
102 static void grp_update (void);
103 static void check_new_gid (void);
104 static void check_new_name (void);
105 static void process_flags (int, char **);
106 static void lock_files (void);
107 static void prepare_failure_reports (void);
108 static void open_files (void);
109 static void close_files (void);
110 static void update_primary_groups (gid_t ogid, gid_t ngid);
111
112 /*
113  * usage - display usage message and exit
114  */
115
116 static void usage (int status)
117 {
118         FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
119         (void) fprintf (usageout,
120                         _("Usage: %s [options] GROUP\n"
121                           "\n"
122                           "Options:\n"),
123                         Prog);
124         (void) fputs (_("  -g, --gid GID                 change the group ID to GID\n"), usageout);
125         (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
126         (void) fputs (_("  -n, --new-name NEW_GROUP      change the name to NEW_GROUP\n"), usageout);
127         (void) fputs (_("  -o, --non-unique              allow to use a duplicate (non-unique) GID\n"), usageout);
128         (void) fputs (_("  -p, --password PASSWORD       change the password to this (encrypted)\n"
129                         "                                PASSWORD\n"), usageout);
130         (void) fputs ("\n", usageout);
131         exit (status);
132 }
133
134 /*
135  * new_grent - updates the values in a group file entry
136  *
137  *      new_grent() takes all of the values that have been entered and fills
138  *      in a (struct group) with them.
139  */
140 static void new_grent (struct group *grent)
141 {
142         if (nflg) {
143                 grent->gr_name = xstrdup (group_newname);
144         }
145
146         if (gflg) {
147                 grent->gr_gid = group_newid;
148         }
149
150         if (   pflg
151 #ifdef SHADOWGRP
152             && (   (!is_shadow_grp)
153                 || (strcmp (grent->gr_passwd, SHADOW_PASSWD_STRING) != 0))
154 #endif
155                 ) {
156                 /* Update the password in group if there is no gshadow
157                  * file or if the password is currently in group
158                  * (gr_passwd != "x").  We do not force the usage of
159                  * shadow passwords if it was not the case before.
160                  */
161                 grent->gr_passwd = group_passwd;
162         }
163 }
164
165 #ifdef  SHADOWGRP
166 /*
167  * new_sgent - updates the values in a shadow group file entry
168  *
169  *      new_sgent() takes all of the values that have been entered and fills
170  *      in a (struct sgrp) with them.
171  */
172 static void new_sgent (struct sgrp *sgent)
173 {
174         if (nflg) {
175                 sgent->sg_name = xstrdup (group_newname);
176         }
177
178         /* Always update the shadowed password if there is a shadow entry
179          * (even if shadowed passwords might not be enabled for this group
180          * (gr_passwd != "x")).
181          * It seems better to update the password in both places in case a
182          * shadow and a non shadow entry exist.
183          * This might occur only if there were already both entries.
184          */
185         if (pflg) {
186                 sgent->sg_passwd = group_passwd;
187         }
188 }
189 #endif                          /* SHADOWGRP */
190
191 /*
192  * grp_update - update group file entries
193  *
194  *      grp_update() updates the new records in the memory databases.
195  */
196 static void grp_update (void)
197 {
198         struct group grp;
199         const struct group *ogrp;
200
201 #ifdef  SHADOWGRP
202         struct sgrp sgrp;
203         const struct sgrp *osgrp = NULL;
204 #endif                          /* SHADOWGRP */
205
206         /*
207          * Get the current settings for this group.
208          */
209         ogrp = gr_locate (group_name);
210         if (NULL == ogrp) {
211                 fprintf (stderr,
212                          _("%s: group '%s' does not exist in %s\n"),
213                          Prog, group_name, gr_dbname ());
214                 exit (E_GRP_UPDATE);
215         }
216         grp = *ogrp;
217         new_grent (&grp);
218 #ifdef  SHADOWGRP
219         if (   is_shadow_grp
220             && (pflg || nflg)) {
221                 osgrp = sgr_locate (group_name);
222                 if (NULL != osgrp) {
223                         sgrp = *osgrp;
224                         new_sgent (&sgrp);
225                 } else if (   pflg
226                            && (strcmp (grp.gr_passwd, SHADOW_PASSWD_STRING) == 0)) {
227                         static char *empty = NULL;
228                         /* If there is a gshadow file with no entries for
229                          * the group, but the group file indicates a
230                          * shadowed password, we force the creation of a
231                          * gshadow entry when a new password is requested.
232                          */
233                         memset (&sgrp, 0, sizeof sgrp);
234                         sgrp.sg_name   = xstrdup (grp.gr_name);
235                         sgrp.sg_passwd = xstrdup (grp.gr_passwd);
236                         sgrp.sg_adm    = &empty;
237                         sgrp.sg_mem    = dup_list (grp.gr_mem);
238                         new_sgent (&sgrp);
239                         osgrp = &sgrp; /* entry needs to be committed */
240                 }
241         }
242 #endif                          /* SHADOWGRP */
243
244         if (gflg) {
245                 update_primary_groups (ogrp->gr_gid, group_newid);
246         }
247
248         /*
249          * Write out the new group file entry.
250          */
251         if (gr_update (&grp) == 0) {
252                 fprintf (stderr,
253                          _("%s: failed to prepare the new %s entry '%s'\n"),
254                          Prog, gr_dbname (), grp.gr_name);
255                 exit (E_GRP_UPDATE);
256         }
257         if (nflg && (gr_remove (group_name) == 0)) {
258                 fprintf (stderr,
259                          _("%s: cannot remove entry '%s' from %s\n"),
260                          Prog, grp.gr_name, gr_dbname ());
261                 exit (E_GRP_UPDATE);
262         }
263
264 #ifdef  SHADOWGRP
265         /*
266          * Make sure there was a shadow entry to begin with.
267          */
268         if (NULL != osgrp) {
269                 /*
270                  * Write out the new shadow group entries as well.
271                  */
272                 if (sgr_update (&sgrp) == 0) {
273                         fprintf (stderr,
274                                  _("%s: failed to prepare the new %s entry '%s'\n"),
275                                  Prog, sgr_dbname (), sgrp.sg_name);
276                         exit (E_GRP_UPDATE);
277                 }
278                 if (nflg && (sgr_remove (group_name) == 0)) {
279                         fprintf (stderr,
280                                  _("%s: cannot remove entry '%s' from %s\n"),
281                                  Prog, group_name, sgr_dbname ());
282                         exit (E_GRP_UPDATE);
283                 }
284         }
285 #endif                          /* SHADOWGRP */
286 }
287
288 /*
289  * check_new_gid - check the new GID value for uniqueness
290  *
291  *      check_new_gid() insures that the new GID value is unique.
292  */
293 static void check_new_gid (void)
294 {
295         /*
296          * First, the easy stuff. If the ID can be duplicated, or if the ID
297          * didn't really change, just return. If the ID didn't change, turn
298          * off those flags. No sense doing needless work.
299          */
300         if (group_id == group_newid) {
301                 gflg = 0;
302                 return;
303         }
304
305         if (oflg ||
306             (getgrgid (group_newid) == NULL) /* local, no need for xgetgrgid */
307            ) {
308                 return;
309         }
310
311         /*
312          * Tell the user what they did wrong.
313          */
314         fprintf (stderr,
315                  _("%s: GID '%lu' already exists\n"),
316                  Prog, (unsigned long int) group_newid);
317         exit (E_GID_IN_USE);
318 }
319
320 /*
321  * check_new_name - check the new name for uniqueness
322  *
323  *      check_new_name() insures that the new name does not exist already.
324  *      You can't have the same name twice, period.
325  */
326 static void check_new_name (void)
327 {
328         /*
329          * Make sure they are actually changing the name.
330          */
331         if (strcmp (group_name, group_newname) == 0) {
332                 nflg = 0;
333                 return;
334         }
335
336         if (is_valid_group_name (group_newname)) {
337
338                 /*
339                  * If the entry is found, too bad.
340                  */
341                 /* local, no need for xgetgrnam */
342                 if (getgrnam (group_newname) != NULL) {
343                         fprintf (stderr,
344                                  _("%s: group '%s' already exists\n"),
345                                  Prog, group_newname);
346                         exit (E_NAME_IN_USE);
347                 }
348                 return;
349         }
350
351         /*
352          * All invalid group names land here.
353          */
354
355         fprintf (stderr,
356                  _("%s: invalid group name '%s'\n"),
357                  Prog, group_newname);
358         exit (E_BAD_ARG);
359 }
360
361 /*
362  * process_flags - perform command line argument setting
363  *
364  *      process_flags() interprets the command line arguments and sets the
365  *      values that the user will be created with accordingly. The values
366  *      are checked for sanity.
367  */
368 static void process_flags (int argc, char **argv)
369 {
370         int option_index = 0;
371         int c;
372         static struct option long_options[] = {
373                 {"gid", required_argument, NULL, 'g'},
374                 {"help", no_argument, NULL, 'h'},
375                 {"new-name", required_argument, NULL, 'n'},
376                 {"non-unique", no_argument, NULL, 'o'},
377                 {"password", required_argument, NULL, 'p'},
378                 {NULL, 0, NULL, '\0'}
379         };
380         while ((c =
381                 getopt_long (argc, argv, "g:hn:op:",
382                              long_options, &option_index)) != -1) {
383                 switch (c) {
384                 case 'g':
385                         gflg = true;
386                         if (   (get_gid (optarg, &group_newid) == 0)
387                             || (group_newid == (gid_t)-1)) {
388                                 fprintf (stderr,
389                                          _("%s: invalid group ID '%s'\n"),
390                                          Prog, optarg);
391                                 exit (E_BAD_ARG);
392                         }
393                         break;
394                 case 'h':
395                         usage (E_SUCCESS);
396                         break;
397                 case 'n':
398                         nflg = true;
399                         group_newname = optarg;
400                         break;
401                 case 'o':
402                         oflg = true;
403                         break;
404                 case 'p':
405                         group_passwd = optarg;
406                         pflg = true;
407                         break;
408                 default:
409                         usage (E_USAGE);
410                 }
411         }
412
413         if (oflg && !gflg) {
414                 usage (E_USAGE);
415         }
416
417         if (optind != (argc - 1)) {
418                 usage (E_USAGE);
419         }
420
421         group_name = argv[argc - 1];
422 }
423
424 /*
425  * close_files - close all of the files that were opened
426  *
427  *      close_files() closes all of the files that were opened for this new
428  *      group. This causes any modified entries to be written out.
429  */
430 static void close_files (void)
431 {
432         if (gr_close () == 0) {
433                 fprintf (stderr,
434                          _("%s: failure while writing changes to %s\n"),
435                          Prog, gr_dbname ());
436                 exit (E_GRP_UPDATE);
437         }
438 #ifdef WITH_AUDIT
439         audit_logger (AUDIT_USER_ACCT, Prog,
440                       info_group.audit_msg,
441                       group_name, AUDIT_NO_ID,
442                       SHADOW_AUDIT_SUCCESS);
443 #endif
444         SYSLOG ((LOG_INFO,
445                  "group changed in %s (%s)",
446                  gr_dbname (), info_group.action));
447         del_cleanup (cleanup_report_mod_group);
448
449         cleanup_unlock_group (NULL);
450         del_cleanup (cleanup_unlock_group);
451
452 #ifdef  SHADOWGRP
453         if (   is_shadow_grp
454             && (pflg || nflg)) {
455                 if (sgr_close () == 0) {
456                         fprintf (stderr,
457                                  _("%s: failure while writing changes to %s\n"),
458                                  Prog, sgr_dbname ());
459                         exit (E_GRP_UPDATE);
460                 }
461 #ifdef WITH_AUDIT
462                 audit_logger (AUDIT_USER_ACCT, Prog,
463                               info_gshadow.audit_msg,
464                               group_name, AUDIT_NO_ID,
465                               SHADOW_AUDIT_SUCCESS);
466 #endif
467                 SYSLOG ((LOG_INFO,
468                          "group changed in %s (%s)",
469                          sgr_dbname (), info_gshadow.action));
470                 del_cleanup (cleanup_report_mod_gshadow);
471
472                 cleanup_unlock_gshadow (NULL);
473                 del_cleanup (cleanup_unlock_gshadow);
474         }
475 #endif                          /* SHADOWGRP */
476
477         if (gflg) {
478                 if (pw_close () == 0) {
479                         fprintf (stderr,
480                                  _("%s: failure while writing changes to %s\n"),
481                                  Prog, pw_dbname ());
482                         exit (E_GRP_UPDATE);
483                 }
484 #ifdef WITH_AUDIT
485                 audit_logger (AUDIT_USER_ACCT, Prog,
486                               info_passwd.audit_msg,
487                               group_name, AUDIT_NO_ID,
488                               SHADOW_AUDIT_SUCCESS);
489 #endif
490                 SYSLOG ((LOG_INFO,
491                          "group changed in %s (%s)",
492                          pw_dbname (), info_passwd.action));
493                 del_cleanup (cleanup_report_mod_passwd);
494
495                 cleanup_unlock_passwd (NULL);
496                 del_cleanup (cleanup_unlock_passwd);
497         }
498
499 #ifdef WITH_AUDIT
500         audit_logger (AUDIT_USER_ACCT, Prog,
501                       "modifying group",
502                       group_name, AUDIT_NO_ID,
503                       SHADOW_AUDIT_SUCCESS);
504 #endif
505 }
506
507 /*
508  * prepare_failure_reports - Prepare the cleanup_info structure for logging
509  * of success and failure to syslog or audit.
510  */
511 static void prepare_failure_reports (void)
512 {
513         info_group.name   = group_name;
514 #ifdef  SHADOWGRP
515         info_gshadow.name = group_name;
516 #endif
517         info_passwd.name  = group_name;
518
519         info_group.audit_msg   = xmalloc (512);
520 #ifdef  SHADOWGRP
521         info_gshadow.audit_msg = xmalloc (512);
522 #endif
523         info_passwd.audit_msg  = xmalloc (512);
524
525         (void) snprintf (info_group.audit_msg, 511,
526                          "changing %s; ", gr_dbname ());
527 #ifdef  SHADOWGRP
528         (void) snprintf (info_gshadow.audit_msg, 511,
529                          "changing %s; ", sgr_dbname ());
530 #endif
531         (void) snprintf (info_passwd.audit_msg, 511,
532                          "changing %s; ", pw_dbname ());
533
534         info_group.action   =   info_group.audit_msg
535                               + strlen (info_group.audit_msg);
536 #ifdef  SHADOWGRP
537         info_gshadow.action =   info_gshadow.audit_msg
538                               + strlen (info_gshadow.audit_msg);
539 #endif
540         info_passwd.action  =   info_passwd.audit_msg
541                               + strlen (info_passwd.audit_msg);
542
543         (void) snprintf (info_group.action,
544                          511 - strlen (info_group.audit_msg),
545                          "group %s/%lu",
546                          group_name, (unsigned long int) group_id);
547 #ifdef  SHADOWGRP
548         (void) snprintf (info_gshadow.action,
549                          511 - strlen (info_group.audit_msg),
550                          "group %s", group_name);
551 #endif
552         (void) snprintf (info_passwd.action,
553                          511 - strlen (info_group.audit_msg),
554                          "group %s/%lu",
555                          group_name, (unsigned long int) group_id);
556
557         if (nflg) {
558                 strncat (info_group.action, ", new name: ",
559                          511 - strlen (info_group.audit_msg));
560                 strncat (info_group.action, group_newname,
561                          511 - strlen (info_group.audit_msg));
562
563 #ifdef  SHADOWGRP
564                 strncat (info_gshadow.action, ", new name: ",
565                          511 - strlen (info_gshadow.audit_msg));
566                 strncat (info_gshadow.action, group_newname,
567                          511 - strlen (info_gshadow.audit_msg));
568 #endif
569
570                 strncat (info_passwd.action, ", new name: ",
571                          511 - strlen (info_passwd.audit_msg));
572                 strncat (info_passwd.action, group_newname,
573                          511 - strlen (info_passwd.audit_msg));
574         }
575         if (pflg) {
576                 strncat (info_group.action, ", new password",
577                          511 - strlen (info_group.audit_msg));
578
579 #ifdef  SHADOWGRP
580                 strncat (info_gshadow.action, ", new password",
581                          511 - strlen (info_gshadow.audit_msg));
582 #endif
583         }
584         if (gflg) {
585                 strncat (info_group.action, ", new gid: ",
586                          511 - strlen (info_group.audit_msg));
587                 (void) snprintf (info_group.action+strlen (info_group.action),
588                                  511 - strlen (info_group.audit_msg),
589                                  "%lu", (unsigned long int) group_newid);
590
591                 strncat (info_passwd.action, ", new gid: ",
592                          511 - strlen (info_passwd.audit_msg));
593                 (void) snprintf (info_passwd.action+strlen (info_passwd.action),
594                                  511 - strlen (info_passwd.audit_msg),
595                                  "%lu", (unsigned long int) group_newid);
596         }
597         info_group.audit_msg[511]   = '\0';
598 #ifdef  SHADOWGRP
599         info_gshadow.audit_msg[511] = '\0';
600 #endif
601         info_passwd.audit_msg[511]  = '\0';
602
603 // FIXME: add a system cleanup
604         add_cleanup (cleanup_report_mod_group, &info_group);
605 #ifdef  SHADOWGRP
606         if (   is_shadow_grp
607             && (pflg || nflg)) {
608                 add_cleanup (cleanup_report_mod_gshadow, &info_gshadow);
609         }
610 #endif
611         if (gflg) {
612                 add_cleanup (cleanup_report_mod_passwd, &info_passwd);
613         }
614
615 }
616
617 /*
618  * lock_files - lock the accounts databases
619  *
620  *      lock_files() locks the group, gshadow, and passwd databases.
621  */
622 static void lock_files (void)
623 {
624         if (gr_lock () == 0) {
625                 fprintf (stderr,
626                          _("%s: cannot lock %s; try again later.\n"),
627                          Prog, gr_dbname ());
628                 exit (E_GRP_UPDATE);
629         }
630         add_cleanup (cleanup_unlock_group, NULL);
631
632 #ifdef  SHADOWGRP
633         if (   is_shadow_grp
634             && (pflg || nflg)) {
635                 if (sgr_lock () == 0) {
636                         fprintf (stderr,
637                                  _("%s: cannot lock %s; try again later.\n"),
638                                  Prog, sgr_dbname ());
639                         exit (E_GRP_UPDATE);
640                 }
641                 add_cleanup (cleanup_unlock_gshadow, NULL);
642         }
643 #endif
644
645         if (gflg) {
646                 if (pw_lock () == 0) {
647                         fprintf (stderr,
648                                  _("%s: cannot lock %s; try again later.\n"),
649                                  Prog, pw_dbname ());
650                         exit (E_GRP_UPDATE);
651                 }
652                 add_cleanup (cleanup_unlock_passwd, NULL);
653         }
654 }
655
656
657 /*
658  * open_files - open the accounts databases
659  *
660  *      open_files() opens the group, gshadow, and passwd databases.
661  */
662 static void open_files (void)
663 {
664         if (gr_open (O_RDWR) == 0) {
665                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
666                 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
667                 exit (E_GRP_UPDATE);
668         }
669
670 #ifdef  SHADOWGRP
671         if (   is_shadow_grp
672             && (pflg || nflg)) {
673                 if (sgr_open (O_RDWR) == 0) {
674                         fprintf (stderr,
675                                  _("%s: cannot open %s\n"),
676                                  Prog, sgr_dbname ());
677                         SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
678                         exit (E_GRP_UPDATE);
679                 }
680         }
681 #endif                          /* SHADOWGRP */
682
683         if (gflg) {
684                 if (pw_open (O_RDWR) == 0) {
685                         fprintf (stderr,
686                                  _("%s: cannot open %s\n"),
687                                  Prog, pw_dbname ());
688                         SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
689                         exit (E_GRP_UPDATE);
690                 }
691         }
692 }
693
694 void update_primary_groups (gid_t ogid, gid_t ngid)
695 {
696         struct passwd *pwd;
697
698         setpwent ();
699         while ((pwd = getpwent ()) != NULL) {
700                 if (pwd->pw_gid == ogid) {
701                         const struct passwd *lpwd;
702                         struct passwd npwd;
703                         lpwd = pw_locate (pwd->pw_name);
704                         if (NULL == lpwd) {
705                                 fprintf (stderr,
706                                          _("%s: user '%s' does not exist in %s\n"),
707                                          Prog, pwd->pw_name, pw_dbname ());
708                                 exit (E_GRP_UPDATE);
709                         } else {
710                                 npwd = *lpwd;
711                                 npwd.pw_gid = ngid;
712                                 if (pw_update (&npwd) == 0) {
713                                         fprintf (stderr,
714                                                  _("%s: failed to prepare the new %s entry '%s'\n"),
715                                                  Prog, pw_dbname (), npwd.pw_name);
716                                         exit (E_GRP_UPDATE);
717                                 }
718                         }
719                 }
720         }
721         endpwent ();
722 }
723
724 /*
725  * main - groupmod command
726  *
727  */
728 int main (int argc, char **argv)
729 {
730 #ifdef ACCT_TOOLS_SETUID
731 #ifdef USE_PAM
732         pam_handle_t *pamh = NULL;
733         int retval;
734 #endif                          /* USE_PAM */
735 #endif                          /* ACCT_TOOLS_SETUID */
736
737 #ifdef WITH_AUDIT
738         audit_help_open ();
739 #endif
740
741         /*
742          * Get my name so that I can use it to report errors.
743          */
744         Prog = Basename (argv[0]);
745
746         (void) setlocale (LC_ALL, "");
747         (void) bindtextdomain (PACKAGE, LOCALEDIR);
748         (void) textdomain (PACKAGE);
749
750         if (atexit (do_cleanups) != 0) {
751                 fprintf (stderr,
752                          _("%s: Cannot setup cleanup service.\n"),
753                          Prog);
754                 exit (1);
755         }
756
757         process_flags (argc, argv);
758
759         OPENLOG ("groupmod");
760
761 #ifdef ACCT_TOOLS_SETUID
762 #ifdef USE_PAM
763         {
764                 struct passwd *pampw;
765                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
766                 if (NULL == pampw) {
767                         fprintf (stderr,
768                                  _("%s: Cannot determine your user name.\n"),
769                                  Prog);
770                         exit (1);
771                 }
772
773                 retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh);
774         }
775
776         if (PAM_SUCCESS == retval) {
777                 retval = pam_authenticate (pamh, 0);
778         }
779
780         if (PAM_SUCCESS == retval) {
781                 retval = pam_acct_mgmt (pamh, 0);
782         }
783
784         if (NULL != pamh) {
785                 (void) pam_end (pamh, retval);
786         }
787         if (PAM_SUCCESS != retval) {
788                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
789                 exit (1);
790         }
791 #endif                          /* USE_PAM */
792 #endif                          /* ACCT_TOOLS_SETUID */
793
794 #ifdef SHADOWGRP
795         is_shadow_grp = sgr_file_present ();
796 #endif
797         {
798                 struct group *grp;
799                 /*
800                  * Start with a quick check to see if the group exists.
801                  */
802                 grp = getgrnam (group_name); /* local, no need for xgetgrnam */
803                 if (NULL == grp) {
804                         fprintf (stderr,
805                                  _("%s: group '%s' does not exist\n"),
806                                  Prog, group_name);
807                         exit (E_NOTFOUND);
808                 } else {
809                         group_id = grp->gr_gid;
810                 }
811         }
812
813 #ifdef  USE_NIS
814         /*
815          * Now make sure it isn't an NIS group.
816          */
817         if (__isgrNIS ()) {
818                 char *nis_domain;
819                 char *nis_master;
820
821                 fprintf (stderr,
822                          _("%s: group %s is a NIS group\n"),
823                          Prog, group_name);
824
825                 if (!yp_get_default_domain (&nis_domain) &&
826                     !yp_master (nis_domain, "group.byname", &nis_master)) {
827                         fprintf (stderr,
828                                  _("%s: %s is the NIS master\n"),
829                                  Prog, nis_master);
830                 }
831                 exit (E_NOTFOUND);
832         }
833 #endif
834
835         if (gflg) {
836                 check_new_gid ();
837         }
838
839         if (nflg) {
840                 check_new_name ();
841         }
842
843         lock_files ();
844
845         /*
846          * Now if the group is not changed, it's our fault.
847          * Make sure failures will be reported.
848          */
849         prepare_failure_reports ();
850
851         /*
852          * Do the hard stuff - open the files, create the group entries,
853          * then close and update the files.
854          */
855         open_files ();
856
857         grp_update ();
858
859         close_files ();
860
861         nscd_flush_cache ("group");
862
863         return E_SUCCESS;
864 }
865