]> granicus.if.org Git - shadow/blob - src/gpasswd.c
Avoid assignment in comparisons.
[shadow] / src / gpasswd.c
1 /*
2  * Copyright 1990 - 1994, Julianne Frances Haugh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #ident "$Id$"
33
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <grp.h>
37 #include <pwd.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <sys/types.h>
41 #include "defines.h"
42 #include "exitcodes.h"
43 #include "groupio.h"
44 #include "nscd.h"
45 #include "prototypes.h"
46 #ifdef SHADOWGRP
47 #include "sgroupio.h"
48 #endif
49 /*
50  * Global variables
51  */
52 /* The name of this command, as it is invoked */
53 static char *Prog;
54
55 #ifdef SHADOWGRP
56 /* Indicate if shadow groups are enabled on the system
57  * (/etc/gshadow present) */
58 static int is_shadowgrp;
59 #endif
60
61 /* Flags set by options */
62 static int
63  aflg = 0, Aflg = 0, dflg = 0, Mflg = 0, rflg = 0, Rflg = 0;
64 /* The name of the group that is being affected */
65 static char *group = NULL;
66 /* The name of the user being added (-a) or removed (-d) from group */
67 static char *user = NULL;
68 /* The new list of members set with -M */
69 static char *members = NULL;
70 #ifdef SHADOWGRP
71 /* The new list of group administrators set with -A */
72 static char *admins = NULL;
73 #endif
74 /* The name of the caller */
75 static char *myname = NULL;
76 /* The UID of the caller */
77 static unsigned long bywho = -1;
78 /* Indicate if gpasswd was called by root */
79 #define amroot  (0 == bywho)
80
81 /* The number of retries for th user to provide and repeat a new password */
82 #ifndef RETRIES
83 #define RETRIES 3
84 #endif
85
86 /* local function prototypes */
87 static void usage (void);
88 static RETSIGTYPE catch_signals (int killed);
89 static int check_list (const char *users);
90 static void process_flags (int argc, char **argv);
91 static void check_flags (int argc, int opt_index);
92 static void open_files (void);
93 static void close_files (void);
94 #ifdef SHADOWGRP
95 static void get_group (struct group *gr, struct sgrp *sg);
96 static void check_perms (const struct sgrp *sg);
97 static void update_group (struct group *gr, struct sgrp *sg);
98 static void change_passwd (struct group *gr, struct sgrp *sg);
99 #else
100 static void get_group (struct group *gr);
101 static void check_perms (const struct group *gr);
102 static void update_group (struct group *gr);
103 static void change_passwd (struct group *gr);
104 #endif
105
106 /*
107  * usage - display usage message
108  */
109 static void usage (void)
110 {
111         fprintf (stderr, _("Usage: %s [-r|-R] group\n"), Prog);
112         fprintf (stderr, _("       %s [-a user] group\n"), Prog);
113         fprintf (stderr, _("       %s [-d user] group\n"), Prog);
114 #ifdef SHADOWGRP
115         fprintf (stderr,
116                  _("       %s [-A user,...] [-M user,...] group\n"), Prog);
117 #else
118         fprintf (stderr, _("       %s [-M user,...] group\n"), Prog);
119 #endif
120         exit (E_USAGE);
121 }
122
123 /*
124  * catch_signals - set or reset termio modes.
125  *
126  *      catch_signals() is called before processing begins. signal() is then
127  *      called with catch_signals() as the signal handler. If signal later
128  *      calls catch_signals() with a signal number, the terminal modes are
129  *      then reset.
130  */
131 static RETSIGTYPE catch_signals (int killed)
132 {
133         static TERMIO sgtty;
134
135         if (killed) {
136                 STTY (0, &sgtty);
137         } else {
138                 GTTY (0, &sgtty);
139         }
140
141         if (killed) {
142                 putchar ('\n');
143                 fflush (stdout);
144                 exit (killed);
145         }
146 }
147
148 /*
149  * check_list - check a comma-separated list of user names for validity
150  *
151  *      check_list scans a comma-separated list of user names and checks
152  *      that each listed name exists.
153  *
154  *      It returns 0 on success.
155  */
156 static int check_list (const char *users)
157 {
158         const char *start, *end;
159         char username[32];
160         int errors = 0;
161         size_t len;
162
163         for (start = users; start && *start; start = end) {
164                 end = strchr (start, ',');
165                 if (NULL != end) {
166                         len = end - start;
167                         end++;
168                 } else {
169                         len = strlen (start);
170                 }
171
172                 if (len > sizeof (username) - 1) {
173                         len = sizeof (username) - 1;
174                 }
175                 strncpy (username, start, len);
176                 username[len] = '\0';
177
178                 /*
179                  * This user must exist.
180                  */
181
182                 if (!getpwnam (username)) { /* local, no need for xgetpwnam */
183                         fprintf (stderr, _("%s: unknown user %s\n"),
184                                  Prog, username);
185                         errors++;
186                 }
187         }
188         return errors;
189 }
190
191 static void failure (void)
192 {
193         fprintf (stderr, _("%s: Permission denied.\n"), Prog);
194         exit (1);
195 }
196
197 /*
198  * process_flags - process the command line options and arguments
199  */
200 static void process_flags (int argc, char **argv)
201 {
202         int flag;
203
204         while ((flag = getopt (argc, argv, "a:A:d:gM:rR")) != EOF) {
205                 switch (flag) {
206                 case 'a':       /* add a user */
207                         user = optarg;
208                         /* local, no need for xgetpwnam */
209                         if (!getpwnam (user)) {
210                                 fprintf (stderr,
211                                          _("%s: unknown user %s\n"), Prog,
212                                          user);
213 #ifdef WITH_AUDIT
214                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
215                                               "adding to group", user, -1, 0);
216 #endif
217                                 exit (1);
218                         }
219                         aflg++;
220                         break;
221 #ifdef SHADOWGRP
222                 case 'A':
223                         if (!amroot) {
224 #ifdef WITH_AUDIT
225                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
226                                               "Listing administrators", NULL,
227                                               bywho, 0);
228 #endif
229                                 failure ();
230                         }
231                         if (!is_shadowgrp) {
232                                 fprintf (stderr,
233                                          _
234                                          ("%s: shadow group passwords required for -A\n"),
235                                          Prog);
236                                 exit (2);
237                         }
238                         admins = optarg;
239                         if (check_list (admins)) {
240                                 exit (1);
241                         }
242                         Aflg++;
243                         break;
244 #endif
245                 case 'd':       /* delete a user */
246                         dflg++;
247                         user = optarg;
248                         break;
249                 case 'g':       /* no-op from normal password */
250                         break;
251                 case 'M':
252                         if (!amroot) {
253 #ifdef WITH_AUDIT
254                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
255                                               "listing members", NULL, bywho,
256                                               0);
257 #endif
258                                 failure ();
259                         }
260                         members = optarg;
261                         if (check_list (members)) {
262                                 exit (1);
263                         }
264                         Mflg++;
265                         break;
266                 case 'r':       /* remove group password */
267                         rflg++;
268                         break;
269                 case 'R':       /* restrict group password */
270                         Rflg++;
271                         break;
272                 default:
273                         usage ();
274                 }
275         }
276
277         /* Get the name of the group that is being affected. */
278         group = argv[optind];
279
280         check_flags (argc, optind);
281 }
282
283 /*
284  * check_flags - check the validity of options
285  */
286 static void check_flags (int argc, int opt_index)
287 {
288         /*
289          * Make sure exclusive flags are exclusive
290          */
291         if (aflg + dflg + rflg + Rflg + (Aflg || Mflg) > 1) {
292                 usage ();
293         }
294
295         /*
296          * Make sure one (and only one) group was provided
297          */
298         if ((argc != (opt_index+1)) || (NULL == group)) {
299                 usage ();
300         }
301 }
302
303 /*
304  * open_files - lock and open the group databases
305  *
306  *      It will call exit in case of error.
307  */
308 static void open_files (void)
309 {
310         if (gr_lock () == 0) {
311                 fprintf (stderr, _("%s: can't get lock\n"), Prog);
312                 SYSLOG ((LOG_WARN, "failed to get lock for /etc/group"));
313 #ifdef WITH_AUDIT
314                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
315                               "locking /etc/group", group, -1, 0);
316 #endif
317                 exit (1);
318         }
319 #ifdef SHADOWGRP
320         if (is_shadowgrp && (sgr_lock () == 0)) {
321                 fprintf (stderr, _("%s: can't get shadow lock\n"), Prog);
322                 SYSLOG ((LOG_WARN, "failed to get lock for /etc/gshadow"));
323 #ifdef WITH_AUDIT
324                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
325                               "locking /etc/gshadow", group, -1, 0);
326 #endif
327                 exit (1);
328         }
329 #endif
330         if (gr_open (O_RDWR) == 0) {
331                 fprintf (stderr, _("%s: can't open file\n"), Prog);
332                 SYSLOG ((LOG_WARN, "cannot open /etc/group"));
333 #ifdef WITH_AUDIT
334                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
335                               "opening /etc/group", group, -1, 0);
336 #endif
337                 exit (1);
338         }
339 #ifdef SHADOWGRP
340         if (is_shadowgrp && (sgr_open (O_RDWR) == 0)) {
341                 fprintf (stderr, _("%s: can't open shadow file\n"), Prog);
342                 SYSLOG ((LOG_WARN, "cannot open /etc/gshadow"));
343 #ifdef WITH_AUDIT
344                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
345                               "opening /etc/gshadow", group, -1, 0);
346 #endif
347                 exit (1);
348         }
349 #endif
350 }
351
352 /*
353  * close_files - close and unlock the group databases
354  *
355  *      This cause any changes in the databases to be committed.
356  *
357  *      It will call exit in case of error.
358  */
359 static void close_files (void)
360 {
361         if (gr_close () == 0) {
362                 fprintf (stderr, _("%s: can't re-write file\n"), Prog);
363                 SYSLOG ((LOG_WARN, "cannot re-write /etc/group"));
364 #ifdef WITH_AUDIT
365                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
366                               "rewriting /etc/group", group, -1, 0);
367 #endif
368                 exit (1);
369         }
370 #ifdef SHADOWGRP
371         if (is_shadowgrp && (sgr_close () == 0)) {
372                 fprintf (stderr, _("%s: can't re-write shadow file\n"), Prog);
373                 SYSLOG ((LOG_WARN, "cannot re-write /etc/gshadow"));
374 #ifdef WITH_AUDIT
375                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
376                               "rewriting /etc/gshadow", group, -1, 0);
377 #endif
378                 exit (1);
379         }
380         if (is_shadowgrp) {
381                 /* TODO: same logging as in open_files & for /etc/group */
382                 sgr_unlock ();
383         }
384 #endif
385         if (gr_unlock () == 0) {
386                 fprintf (stderr, _("%s: can't unlock file\n"), Prog);
387 #ifdef WITH_AUDIT
388                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
389                               "unlocking group file", group, -1, 0);
390 #endif
391                 exit (1);
392         }
393 }
394
395 /*
396  * check_perms - check if the user is allowed to change the password of
397  *               the specified group.
398  *
399  *      It only returns if the user is allowed.
400  */
401 #ifdef SHADOWGRP
402 static void check_perms (const struct sgrp *sg)
403 #else
404 static void check_perms (const struct group *gr)
405 #endif
406 {
407 #ifdef SHADOWGRP
408         /*
409          * The policy here for changing a group is that 1) you must be root
410          * or 2). you must be listed as an administrative member.
411          * Administrative members can do anything to a group that the root
412          * user can.
413          */
414         if (!amroot && !is_on_list (sg->sg_adm, myname)) {
415 #ifdef WITH_AUDIT
416                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
417                               "modify group", group, -1, 0);
418 #endif
419                 failure ();
420         }
421 #else                           /* ! SHADOWGRP */
422
423 #ifdef FIRST_MEMBER_IS_ADMIN
424         /*
425          * The policy here for changing a group is that 1) you must bes root
426          * or 2) you must be the first listed member of the group. The
427          * first listed member of a group can do anything to that group that
428          * the root user can. The rationale for this hack is that the FIRST
429          * user is probably the most important user in this entire group.
430          */
431         if (!amroot) {
432                 if (gr->gr_mem[0] == (char *) 0) {
433 #ifdef WITH_AUDIT
434                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
435                                       "modifying group", group, -1, 0);
436 #endif
437                         failure ();
438                 }
439
440                 if (strcmp (gr->gr_mem[0], myname) != 0) {
441 #ifdef WITH_AUDIT
442                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
443                                       "modifying group", myname, -1, 0);
444 #endif
445                         failure ();
446                 }
447         }
448 #else
449         /*
450          * This feature enabled by default could be a security problem when
451          * installed on existing systems where the first group member might
452          * be just a normal user.  --marekm
453          */
454         if (!amroot) {
455 #ifdef WITH_AUDIT
456                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
457                               "modifying group", group, -1, 0);
458 #endif
459                 failure ();
460         }
461 #endif
462 #endif                          /* SHADOWGRP */
463 }
464
465 /*
466  * update_group - Update the group information in the databases
467  */
468 #ifdef SHADOWGRP
469 static void update_group (struct group *gr, struct sgrp *sg)
470 #else
471 static void update_group (struct group *gr)
472 #endif
473 {
474         if (!gr_update (gr)) {
475                 fprintf (stderr, _("%s: can't update entry\n"), Prog);
476                 SYSLOG ((LOG_WARN, "cannot update /etc/group"));
477 #ifdef WITH_AUDIT
478                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
479                               "updating /etc/group", group, -1, 0);
480 #endif
481                 exit (1);
482         }
483 #ifdef SHADOWGRP
484         if (is_shadowgrp && !sgr_update (sg)) {
485                 fprintf (stderr, _("%s: can't update shadow entry\n"), Prog);
486                 SYSLOG ((LOG_WARN, "cannot update /etc/gshadow"));
487 #ifdef WITH_AUDIT
488                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
489                               "updating /etc/gshadow", group, -1, 0);
490 #endif
491                 exit (1);
492         }
493 #endif
494 }
495
496 /*
497  * get_group - get the current information for the group
498  *
499  *      The information are copied in group structure(s) so that they can be
500  *      modified later.
501  */
502 #ifdef SHADOWGRP
503 static void get_group (struct group *gr, struct sgrp *sg)
504 #else
505 static void get_group (struct group *gr)
506 #endif
507 {
508         struct group const*tmpgr = NULL;
509         struct sgrp const*tmpsg = NULL;
510
511         if (!gr_open (O_RDONLY)) {
512                 fprintf (stderr, _("%s: can't open file\n"), Prog);
513                 SYSLOG ((LOG_WARN, "cannot open /etc/group"));
514 #ifdef WITH_AUDIT
515                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
516                               "opening /etc/group", group, -1, 0);
517 #endif
518                 exit (1);
519         }
520
521         tmpgr = gr_locate (group);
522         if (NULL == tmpgr) {
523                 fprintf (stderr, _("unknown group: %s\n"), group);
524 #ifdef WITH_AUDIT
525                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
526                               "group lookup", group, -1, 0);
527 #endif
528                 failure ();
529         }
530
531         *gr = *tmpgr;
532         gr->gr_name = xstrdup (tmpgr->gr_name);
533         gr->gr_passwd = xstrdup (tmpgr->gr_passwd);
534         gr->gr_mem = dup_list (tmpgr->gr_mem);
535
536         if (!gr_close ()) {
537                 fprintf (stderr, _("%s: can't close file\n"), Prog);
538                 SYSLOG ((LOG_WARN, "cannot close /etc/group"));
539 #ifdef WITH_AUDIT
540                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
541                               "closing /etc/group", group, -1, 0);
542 #endif
543                 exit (1);
544         }
545
546 #ifdef SHADOWGRP
547         if (!sgr_open (O_RDONLY)) {
548                 fprintf (stderr, _("%s: can't open shadow file\n"), Prog);
549                 SYSLOG ((LOG_WARN, "cannot open /etc/gshadow"));
550 #ifdef WITH_AUDIT
551                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
552                               "opening /etc/gshadow", group, -1, 0);
553 #endif
554                 exit (1);
555         }
556         tmpsg = sgr_locate (group);
557         if (NULL != tmpsg) {
558                 *sg = *tmpsg;
559                 sg->sg_name = xstrdup (tmpsg->sg_name);
560                 sg->sg_passwd = xstrdup (tmpsg->sg_passwd);
561
562                 sg->sg_mem = dup_list (tmpsg->sg_mem);
563                 sg->sg_adm = dup_list (tmpsg->sg_adm);
564         } else {
565                 sg->sg_name = xstrdup (group);
566                 sg->sg_passwd = gr->gr_passwd;
567                 gr->gr_passwd = "!";    /* XXX warning: const */
568
569                 sg->sg_mem = dup_list (gr->gr_mem);
570
571                 sg->sg_adm = (char **) xmalloc (sizeof (char *) * 2);
572 #ifdef FIRST_MEMBER_IS_ADMIN
573                 if (sg->sg_mem[0]) {
574                         sg->sg_adm[0] = xstrdup (sg->sg_mem[0]);
575                         sg->sg_adm[1] = 0;
576                 } else
577 #endif
578                 {
579                         sg->sg_adm[0] = 0;
580                 }
581
582         }
583         if (!sgr_close ()) {
584                 fprintf (stderr, _("%s: can't close shadow file\n"), Prog);
585                 SYSLOG ((LOG_WARN, "cannot close /etc/gshadow"));
586 #ifdef WITH_AUDIT
587                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
588                               "closing /etc/gshadow", group, -1, 0);
589 #endif
590                 exit (1);
591         }
592 #endif                          /* SHADOWGRP */
593 }
594
595 /*
596  * change_passwd - change the group's password
597  *
598  *      Get the new password from the user and update the password in the
599  *      group's structure.
600  *
601  *      It will call exit in case of error.
602  */
603 #ifdef SHADOWGRP
604 static void change_passwd (struct group *gr, struct sgrp *sg)
605 #else
606 static void change_passwd (struct group *gr)
607 #endif
608 {
609         char *cp;
610         static char pass[BUFSIZ];
611         int retries;
612
613         /*
614          * A new password is to be entered and it must be encrypted, etc.
615          * The password will be prompted for twice, and both entries must be
616          * identical. There is no need to validate the old password since
617          * the invoker is either the group owner, or root.
618          */
619         printf (_("Changing the password for group %s\n"), group);
620
621         for (retries = 0; retries < RETRIES; retries++) {
622                 cp = getpass (_("New Password: "));
623                 if (NULL == cp) {
624                         exit (1);
625                 }
626
627                 STRFCPY (pass, cp);
628                 strzero (cp);
629                 cp = getpass (_("Re-enter new password: "));
630                 if (NULL == cp) {
631                         exit (1);
632                 }
633
634                 if (strcmp (pass, cp) == 0) {
635                         strzero (cp);
636                         break;
637                 }
638
639                 strzero (cp);
640                 memzero (pass, sizeof pass);
641
642                 if (retries + 1 < RETRIES) {
643                         puts (_("They don't match; try again"));
644 #ifdef WITH_AUDIT
645                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
646                                       "changing password", group, -1, 0);
647 #endif
648                 }
649         }
650
651         if (retries == RETRIES) {
652                 fprintf (stderr, _("%s: Try again later\n"), Prog);
653                 exit (1);
654         }
655
656         cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL));
657         memzero (pass, sizeof pass);
658 #ifdef SHADOWGRP
659         if (is_shadowgrp) {
660                 sg->sg_passwd = cp;
661         } else
662 #endif
663         {
664                 gr->gr_passwd = cp;
665         }
666 #ifdef WITH_AUDIT
667         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
668                       "changing password", group, -1, 1);
669 #endif
670         SYSLOG ((LOG_INFO, "change the password for group %s by %s", group,
671                 myname));
672 }
673
674 /*
675  * gpasswd - administer the /etc/group file
676  *
677  *      -a user         add user to the named group
678  *      -d user         remove user from the named group
679  *      -r              remove password from the named group
680  *      -R              restrict access to the named group
681  *      -A user,...     make list of users the administrative users
682  *      -M user,...     make list of users the group members
683  */
684 int main (int argc, char **argv)
685 {
686         struct group grent;
687 #ifdef SHADOWGRP
688         struct sgrp sgent;
689 #endif
690         struct passwd *pw = NULL;
691
692 #ifdef WITH_AUDIT
693         audit_help_open ();
694 #endif
695
696         sanitize_env ();
697         setlocale (LC_ALL, "");
698         bindtextdomain (PACKAGE, LOCALEDIR);
699         textdomain (PACKAGE);
700
701         /*
702          * Make a note of whether or not this command was invoked by root.
703          * This will be used to bypass certain checks later on. Also, set
704          * the real user ID to match the effective user ID. This will
705          * prevent the invoker from issuing signals which would interfer
706          * with this command.
707          */
708         bywho = getuid ();
709         Prog = Basename (argv[0]);
710
711         OPENLOG ("gpasswd");
712         setbuf (stdout, NULL);
713         setbuf (stderr, NULL);
714
715 #ifdef SHADOWGRP
716         is_shadowgrp = sgr_file_present ();
717 #endif
718
719         /* Parse the options */
720         process_flags (argc, argv);
721
722         /*
723          * Determine the name of the user that invoked this command. This
724          * is really hit or miss because there are so many ways that command
725          * can be executed and so many ways to trip up the routines that
726          * report the user name.
727          */
728
729         pw = get_my_pwent ();
730         if (!pw) {
731                 fprintf (stderr, _("Who are you?\n"));
732 #ifdef WITH_AUDIT
733                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "user lookup", NULL,
734                               bywho, 0);
735 #endif
736                 failure ();
737         }
738         myname = xstrdup (pw->pw_name);
739
740         /*
741          * Replicate the group so it can be modified later on.
742          */
743 #ifdef SHADOWGRP
744         get_group (&grent, &sgent);
745 #else
746         get_group (&grent);
747 #endif
748
749         /*
750          * Check if the user is allowed to change the password of this group.
751          */
752 #ifdef SHADOWGRP
753         check_perms (&sgent);
754 #else
755         check_perms (&grent);
756 #endif
757
758         /*
759          * Removing a password is straight forward. Just set the password
760          * field to a "".
761          */
762         if (rflg) {
763                 grent.gr_passwd = "";   /* XXX warning: const */
764 #ifdef SHADOWGRP
765                 sgent.sg_passwd = "";   /* XXX warning: const */
766 #endif
767 #ifdef WITH_AUDIT
768                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
769                               "deleting group password", group, -1, 1);
770 #endif
771                 SYSLOG ((LOG_INFO, "remove password from group %s by %s",
772                          group, myname));
773                 goto output;
774         } else if (Rflg) {
775                 /*
776                  * Same thing for restricting the group. Set the password
777                  * field to "!".
778                  */
779                 grent.gr_passwd = "!";  /* XXX warning: const */
780 #ifdef SHADOWGRP
781                 sgent.sg_passwd = "!";  /* XXX warning: const */
782 #endif
783 #ifdef WITH_AUDIT
784                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
785                               "restrict access to group", group, -1, 1);
786 #endif
787                 SYSLOG ((LOG_INFO, "restrict access to group %s by %s",
788                          group, myname));
789                 goto output;
790         }
791
792         /*
793          * Adding a member to a member list is pretty straightforward as
794          * well. Call the appropriate routine and split.
795          */
796         if (aflg) {
797                 printf (_("Adding user %s to group %s\n"), user, group);
798                 grent.gr_mem = add_list (grent.gr_mem, user);
799 #ifdef SHADOWGRP
800                 sgent.sg_mem = add_list (sgent.sg_mem, user);
801 #endif
802 #ifdef WITH_AUDIT
803                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group member",
804                               user, -1, 1);
805 #endif
806                 SYSLOG ((LOG_INFO, "add member %s to group %s by %s", user,
807                          group, myname));
808                 goto output;
809         }
810
811         /*
812          * Removing a member from the member list is the same deal as adding
813          * one, except the routine is different.
814          */
815         if (dflg) {
816                 int removed = 0;
817
818                 printf (_("Removing user %s from group %s\n"), user, group);
819
820                 if (is_on_list (grent.gr_mem, user)) {
821                         removed = 1;
822                         grent.gr_mem = del_list (grent.gr_mem, user);
823                 }
824 #ifdef SHADOWGRP
825                 if (is_on_list (sgent.sg_mem, user)) {
826                         removed = 1;
827                         sgent.sg_mem = del_list (sgent.sg_mem, user);
828                 }
829 #endif
830                 if (!removed) {
831                         fprintf (stderr, _("%s: unknown member %s\n"),
832                                  Prog, user);
833 #ifdef WITH_AUDIT
834                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
835                                       "deleting member", user, -1, 0);
836 #endif
837                         exit (1);
838                 }
839 #ifdef WITH_AUDIT
840                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "deleting member",
841                               user, -1, 1);
842 #endif
843                 SYSLOG ((LOG_INFO, "remove member %s from group %s by %s",
844                          user, group, myname));
845                 goto output;
846         }
847 #ifdef SHADOWGRP
848         /*
849          * Replacing the entire list of administators is simple. Check the
850          * list to make sure everyone is a real user. Then slap the new list
851          * in place.
852          */
853         if (Aflg) {
854 #ifdef WITH_AUDIT
855                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "setting group admin",
856                               group, -1, 1);
857 #endif
858                 SYSLOG ((LOG_INFO, "set administrators of %s to %s",
859                          group, admins));
860                 sgent.sg_adm = comma_to_list (admins);
861                 if (!Mflg) {
862                         goto output;
863                 }
864         }
865 #endif
866
867         /*
868          * Replacing the entire list of members is simple. Check the list to
869          * make sure everyone is a real user. Then slap the new list in
870          * place.
871          */
872         if (Mflg) {
873 #ifdef WITH_AUDIT
874                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
875                               "setting group members", group, -1, 1);
876 #endif
877                 SYSLOG ((LOG_INFO, "set members of %s to %s", group, members));
878 #ifdef SHADOWGRP
879                 sgent.sg_mem = comma_to_list (members);
880 #endif
881                 grent.gr_mem = comma_to_list (members);
882                 goto output;
883         }
884
885         /*
886          * If the password is being changed, the input and output must both
887          * be a tty. The typical keyboard signals are caught so the termio
888          * modes can be restored.
889          */
890         if (!isatty (0) || !isatty (1)) {
891                 fprintf (stderr, _("%s: Not a tty\n"), Prog);
892 #ifdef WITH_AUDIT
893                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing password",
894                               group, -1, 0);
895 #endif
896                 exit (1);
897         }
898
899         catch_signals (0);      /* save tty modes */
900
901         signal (SIGHUP, catch_signals);
902         signal (SIGINT, catch_signals);
903         signal (SIGQUIT, catch_signals);
904         signal (SIGTERM, catch_signals);
905 #ifdef SIGTSTP
906         signal (SIGTSTP, catch_signals);
907 #endif
908
909         /* Prompt for the new password */
910 #ifdef SHADOWGRP
911         change_passwd (&grent, &sgent);
912 #else
913         change_passwd (&grent);
914 #endif
915
916         /*
917          * This is the common arrival point to output the new group file.
918          * The freshly crafted entry is in allocated space. The group file
919          * will be locked and opened for writing. The new entry will be
920          * output, etc.
921          */
922       output:
923         if (setuid (0)) {
924                 fprintf (stderr, _("Cannot change ID to root.\n"));
925                 SYSLOG ((LOG_ERR, "can't setuid(0)"));
926 #ifdef WITH_AUDIT
927                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "changing id to root",
928                               group, -1, 0);
929 #endif
930                 closelog ();
931                 exit (1);
932         }
933         pwd_init ();
934
935         open_files ();
936
937 #ifdef SHADOWGRP
938         update_group (&grent, &sgent);
939 #else
940         update_group (&grent);
941 #endif
942
943         close_files ();
944
945         nscd_flush_cache ("group");
946
947         exit (E_SUCCESS);
948 }