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