]> granicus.if.org Git - shadow/blob - src/gpasswd.c
* src/chage.c, src/chgpasswd.c, src/chpasswd.c, src/chsh.c,
[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,
372                          _("%s: cannot lock %s; try again later.\n"),
373                          Prog, gr_dbname ());
374 #ifdef WITH_AUDIT
375                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
376                               "locking /etc/group",
377                               group, AUDIT_NO_ID, 0);
378 #endif
379                 fail_exit (1);
380         }
381         group_locked = true;
382 #ifdef SHADOWGRP
383         if (is_shadowgrp) {
384                 if (sgr_lock () == 0) {
385                         fprintf (stderr,
386                                  _("%s: cannot lock %s; try again later.\n"),
387                                  Prog, sgr_dbname ());
388 #ifdef WITH_AUDIT
389                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
390                                       "locking /etc/gshadow",
391                                       group, AUDIT_NO_ID, 0);
392 #endif
393                         fail_exit (1);
394                 }
395                 gshadow_locked = true;
396         }
397 #endif
398         if (gr_open (O_RDWR) == 0) {
399                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
400                 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
401 #ifdef WITH_AUDIT
402                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
403                               "opening /etc/group",
404                               group, AUDIT_NO_ID, 0);
405 #endif
406                 fail_exit (1);
407         }
408 #ifdef SHADOWGRP
409         if (is_shadowgrp && (sgr_open (O_RDWR) == 0)) {
410                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ());
411                 SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
412 #ifdef WITH_AUDIT
413                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
414                               "opening /etc/gshadow",
415                               group, AUDIT_NO_ID, 0);
416 #endif
417                 fail_exit (1);
418         }
419 #endif
420 }
421
422 /*
423  * close_files - close and unlock the group databases
424  *
425  *      This cause any changes in the databases to be committed.
426  *
427  *      It will call exit in case of error.
428  */
429 static void close_files (void)
430 {
431         if (gr_close () == 0) {
432                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
433                 SYSLOG ((LOG_WARN, "failure while writing changes to %s", gr_dbname ()));
434 #ifdef WITH_AUDIT
435                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
436                               "rewriting /etc/group",
437                               group, AUDIT_NO_ID, 0);
438 #endif
439                 fail_exit (1);
440         }
441 #ifdef SHADOWGRP
442         if (is_shadowgrp) {
443                 if (sgr_close () == 0) {
444                         fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
445                         SYSLOG ((LOG_WARN, "failure while writing changes to %s", sgr_dbname ()));
446 #ifdef WITH_AUDIT
447                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
448                                       "rewriting /etc/gshadow",
449                                       group, AUDIT_NO_ID, 0);
450 #endif
451                         fail_exit (1);
452                 }
453                 if (sgr_unlock () == 0) {
454                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
455                         SYSLOG ((LOG_WARN, "failed to unlock %s", sgr_dbname ()));
456 #ifdef WITH_AUDIT
457                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
458                                       "unlocking gshadow file",
459                                       group, AUDIT_NO_ID, 0);
460 #endif
461                         /* continue */
462                 }
463                 gshadow_locked = false;
464         }
465 #endif
466         if (gr_unlock () == 0) {
467                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
468                 SYSLOG ((LOG_WARN, "failed to unlock %s", gr_dbname ()));
469 #ifdef WITH_AUDIT
470                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
471                               "unlocking group file",
472                               group, AUDIT_NO_ID, 0);
473 #endif
474                 /* continue */
475         }
476         group_locked = false;
477 }
478
479 /*
480  * check_perms - check if the user is allowed to change the password of
481  *               the specified group.
482  *
483  *      It only returns if the user is allowed.
484  */
485 #ifdef SHADOWGRP
486 static void check_perms (const struct group *gr, const struct sgrp *sg)
487 #else
488 static void check_perms (const struct group *gr)
489 #endif
490 {
491 #ifdef SHADOWGRP
492         if (is_shadowgrp) {
493                 /*
494                  * The policy here for changing a group is that
495                  * 1) you must be root or
496                  * 2) you must be listed as an administrative member.
497                  * Administrative members can do anything to a group that
498                  * the root user can.
499                  */
500                 if (!amroot && !is_on_list (sg->sg_adm, myname)) {
501 #ifdef WITH_AUDIT
502                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
503                                       "modify group",
504                                       group, AUDIT_NO_ID, 0);
505 #endif
506                         failure ();
507                 }
508         } else
509 #endif                          /* ! SHADOWGRP */
510         {
511 #ifdef FIRST_MEMBER_IS_ADMIN
512                 /*
513                  * The policy here for changing a group is that
514                  * 1) you must be root or
515                  * 2) you must be the first listed member of the group.
516                  * The first listed member of a group can do anything to
517                  * that group that the root user can. The rationale for
518                  * this hack is that the FIRST user is probably the most
519                  * important user in this entire group.
520                  *
521                  * This feature enabled by default could be a security
522                  * problem when installed on existing systems where the
523                  * first group member might be just a normal user.
524                  * --marekm
525                  */
526                 if (!amroot) {
527                         if (gr->gr_mem[0] == (char *) 0) {
528 #ifdef WITH_AUDIT
529                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
530                                               "modifying group",
531                                               group, AUDIT_NO_ID, 0);
532 #endif
533                                 failure ();
534                         }
535
536                         if (strcmp (gr->gr_mem[0], myname) != 0) {
537 #ifdef WITH_AUDIT
538                                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
539                                               "modifying group",
540                                               myname, AUDIT_NO_ID, 0);
541 #endif
542                                 failure ();
543                         }
544                 }
545 #else                           /* ! FIRST_MEMBER_IS_ADMIN */
546                 if (!amroot) {
547 #ifdef WITH_AUDIT
548                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
549                                       "modifying group",
550                                       group, AUDIT_NO_ID, 0);
551 #endif
552                         failure ();
553                 }
554 #endif
555         }
556 }
557
558 /*
559  * update_group - Update the group information in the databases
560  */
561 #ifdef SHADOWGRP
562 static void update_group (struct group *gr, struct sgrp *sg)
563 #else
564 static void update_group (struct group *gr)
565 #endif
566 {
567         if (gr_update (gr) == 0) {
568                 fprintf (stderr,
569                          _("%s: cannot update entry '%s' in %s\n"),
570                          Prog, gr->gr_name, gr_dbname ());
571                 SYSLOG ((LOG_WARN, "cannot update entry '%s' in %s", gr->gr_name, gr_dbname ()));
572 #ifdef WITH_AUDIT
573                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
574                               "updating /etc/group",
575                               group, AUDIT_NO_ID, 0);
576 #endif
577                 fail_exit (1);
578         }
579 #ifdef SHADOWGRP
580         if (is_shadowgrp && (sgr_update (sg) == 0)) {
581                 fprintf (stderr, _("%s: cannot update entry '%s' in %s\n"), Prog, sg->sg_name, sgr_dbname ());
582                 SYSLOG ((LOG_WARN, "cannot update entry '%s' in %s", sg->sg_name, sgr_dbname ()));
583 #ifdef WITH_AUDIT
584                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
585                               "updating /etc/gshadow",
586                               group, AUDIT_NO_ID, 0);
587 #endif
588                 fail_exit (1);
589         }
590 #endif
591 }
592
593 /*
594  * get_group - get the current information for the group
595  *
596  *      The information are copied in group structure(s) so that they can be
597  *      modified later.
598  *
599  *      Note: If !is_shadowgrp, *sg will not be initialized.
600  */
601 #ifdef SHADOWGRP
602 static void get_group (struct group *gr, struct sgrp *sg)
603 #else
604 static void get_group (struct group *gr)
605 #endif
606 {
607         struct group const*tmpgr = NULL;
608         struct sgrp const*tmpsg = NULL;
609
610         if (gr_open (O_RDONLY) == 0) {
611                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
612                 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
613 #ifdef WITH_AUDIT
614                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
615                               "opening /etc/group",
616                               group, AUDIT_NO_ID, 0);
617 #endif
618                 fail_exit (1);
619         }
620
621         tmpgr = gr_locate (group);
622         if (NULL == tmpgr) {
623                 fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), Prog, group, gr_dbname ());
624 #ifdef WITH_AUDIT
625                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
626                               "group lookup",
627                               group, AUDIT_NO_ID, 0);
628 #endif
629                 failure ();
630         }
631
632         *gr = *tmpgr;
633         gr->gr_name = xstrdup (tmpgr->gr_name);
634         gr->gr_passwd = xstrdup (tmpgr->gr_passwd);
635         gr->gr_mem = dup_list (tmpgr->gr_mem);
636
637         if (gr_close () == 0) {
638                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
639                 SYSLOG ((LOG_WARN, "failure while writing changes to %s", gr_dbname ()));
640 #ifdef WITH_AUDIT
641                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
642                               "closing /etc/group",
643                               group, AUDIT_NO_ID, 0);
644 #endif
645                 fail_exit (1);
646         }
647
648 #ifdef SHADOWGRP
649         if (is_shadowgrp) {
650                 if (sgr_open (O_RDONLY) == 0) {
651                         fprintf (stderr,
652                                  _("%s: cannot open %s\n"), Prog, sgr_dbmane ());
653                         SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
654 #ifdef WITH_AUDIT
655                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
656                                       "opening /etc/gshadow",
657                                       group, AUDIT_NO_ID, 0);
658 #endif
659                         fail_exit (1);
660                 }
661                 tmpsg = sgr_locate (group);
662                 if (NULL != tmpsg) {
663                         *sg = *tmpsg;
664                         sg->sg_name = xstrdup (tmpsg->sg_name);
665                         sg->sg_passwd = xstrdup (tmpsg->sg_passwd);
666
667                         sg->sg_mem = dup_list (tmpsg->sg_mem);
668                         sg->sg_adm = dup_list (tmpsg->sg_adm);
669                 } else {
670                         sg->sg_name = xstrdup (group);
671                         sg->sg_passwd = gr->gr_passwd;
672                         gr->gr_passwd = SHADOW_PASSWD_STRING;   /* XXX warning: const */
673
674                         sg->sg_mem = dup_list (gr->gr_mem);
675
676                         sg->sg_adm = (char **) xmalloc (sizeof (char *) * 2);
677 #ifdef FIRST_MEMBER_IS_ADMIN
678                         if (sg->sg_mem[0]) {
679                                 sg->sg_adm[0] = xstrdup (sg->sg_mem[0]);
680                                 sg->sg_adm[1] = NULL;
681                         } else
682 #endif
683                         {
684                                 sg->sg_adm[0] = NULL;
685                         }
686
687                 }
688                 if (sgr_close () == 0) {
689                         fprintf (stderr,
690                                  _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
691                         SYSLOG ((LOG_WARN, "failure while writing changes to %s", sgr_dbname ()));
692 #ifdef WITH_AUDIT
693                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
694                                       "closing /etc/gshadow",
695                                       group, AUDIT_NO_ID, 0);
696 #endif
697                         fail_exit (1);
698                 }
699         }
700 #endif                          /* SHADOWGRP */
701 }
702
703 /*
704  * change_passwd - change the group's password
705  *
706  *      Get the new password from the user and update the password in the
707  *      group's structure.
708  *
709  *      It will call exit in case of error.
710  */
711 #ifdef SHADOWGRP
712 static void change_passwd (struct group *gr, struct sgrp *sg)
713 #else
714 static void change_passwd (struct group *gr)
715 #endif
716 {
717         char *cp;
718         static char pass[BUFSIZ];
719         int retries;
720
721         /*
722          * A new password is to be entered and it must be encrypted, etc.
723          * The password will be prompted for twice, and both entries must be
724          * identical. There is no need to validate the old password since
725          * the invoker is either the group owner, or root.
726          */
727         printf (_("Changing the password for group %s\n"), group);
728
729         for (retries = 0; retries < RETRIES; retries++) {
730                 cp = getpass (_("New Password: "));
731                 if (NULL == cp) {
732                         fail_exit (1);
733                 }
734
735                 STRFCPY (pass, cp);
736                 strzero (cp);
737                 cp = getpass (_("Re-enter new password: "));
738                 if (NULL == cp) {
739                         fail_exit (1);
740                 }
741
742                 if (strcmp (pass, cp) == 0) {
743                         strzero (cp);
744                         break;
745                 }
746
747                 strzero (cp);
748                 memzero (pass, sizeof pass);
749
750                 if (retries + 1 < RETRIES) {
751                         puts (_("They don't match; try again"));
752 #ifdef WITH_AUDIT
753                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
754                                       "changing password",
755                                       group, AUDIT_NO_ID, 0);
756 #endif
757                 }
758         }
759
760         if (retries == RETRIES) {
761                 fprintf (stderr, _("%s: Try again later\n"), Prog);
762                 fail_exit (1);
763         }
764
765         cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL));
766         memzero (pass, sizeof pass);
767 #ifdef SHADOWGRP
768         if (is_shadowgrp) {
769                 sg->sg_passwd = cp;
770         } else
771 #endif
772         {
773                 gr->gr_passwd = cp;
774         }
775 #ifdef WITH_AUDIT
776         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
777                       "changing password",
778                       group, AUDIT_NO_ID, 1);
779 #endif
780         SYSLOG ((LOG_INFO, "change the password for group %s by %s", group,
781                 myname));
782 }
783
784 /*
785  * gpasswd - administer the /etc/group file
786  *
787  *      -a user         add user to the named group
788  *      -d user         remove user from the named group
789  *      -r              remove password from the named group
790  *      -R              restrict access to the named group
791  *      -A user,...     make list of users the administrative users
792  *      -M user,...     make list of users the group members
793  */
794 int main (int argc, char **argv)
795 {
796         struct group grent;
797 #ifdef SHADOWGRP
798         struct sgrp sgent;
799 #endif
800         struct passwd *pw = NULL;
801
802 #ifdef WITH_AUDIT
803         audit_help_open ();
804 #endif
805
806         sanitize_env ();
807         (void) setlocale (LC_ALL, "");
808         (void) bindtextdomain (PACKAGE, LOCALEDIR);
809         (void) textdomain (PACKAGE);
810
811         /*
812          * Make a note of whether or not this command was invoked by root.
813          * This will be used to bypass certain checks later on. Also, set
814          * the real user ID to match the effective user ID. This will
815          * prevent the invoker from issuing signals which would interfer
816          * with this command.
817          */
818         bywho = getuid ();
819         Prog = Basename (argv[0]);
820
821         OPENLOG ("gpasswd");
822         setbuf (stdout, NULL);
823         setbuf (stderr, NULL);
824
825 #ifdef SHADOWGRP
826         is_shadowgrp = sgr_file_present ();
827 #endif
828
829         /* Parse the options */
830         process_flags (argc, argv);
831
832         /*
833          * Determine the name of the user that invoked this command. This
834          * is really hit or miss because there are so many ways that command
835          * can be executed and so many ways to trip up the routines that
836          * report the user name.
837          */
838
839         pw = get_my_pwent ();
840         if (NULL == pw) {
841                 fputs (_("Who are you?\n"), stderr);
842 #ifdef WITH_AUDIT
843                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
844                               "user lookup",
845                               NULL, (unsigned int) bywho, 0);
846 #endif
847                 failure ();
848         }
849         myname = xstrdup (pw->pw_name);
850
851         /*
852          * Replicate the group so it can be modified later on.
853          */
854 #ifdef SHADOWGRP
855         get_group (&grent, &sgent);
856 #else
857         get_group (&grent);
858 #endif
859
860         /*
861          * Check if the user is allowed to change the password of this group.
862          */
863 #ifdef SHADOWGRP
864         check_perms (&grent, &sgent);
865 #else
866         check_perms (&grent);
867 #endif
868
869         /*
870          * Removing a password is straight forward. Just set the password
871          * field to a "".
872          */
873         if (rflg) {
874                 grent.gr_passwd = "";   /* XXX warning: const */
875 #ifdef SHADOWGRP
876                 sgent.sg_passwd = "";   /* XXX warning: const */
877 #endif
878 #ifdef WITH_AUDIT
879                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
880                               "deleting group password",
881                               group, AUDIT_NO_ID, 1);
882 #endif
883                 SYSLOG ((LOG_INFO, "remove password from group %s by %s",
884                          group, myname));
885                 goto output;
886         } else if (Rflg) {
887                 /*
888                  * Same thing for restricting the group. Set the password
889                  * field to "!".
890                  */
891                 grent.gr_passwd = "!";  /* XXX warning: const */
892 #ifdef SHADOWGRP
893                 sgent.sg_passwd = "!";  /* XXX warning: const */
894 #endif
895 #ifdef WITH_AUDIT
896                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
897                               "restrict access to group",
898                               group, AUDIT_NO_ID, 1);
899 #endif
900                 SYSLOG ((LOG_INFO, "restrict access to group %s by %s",
901                          group, myname));
902                 goto output;
903         }
904
905         /*
906          * Adding a member to a member list is pretty straightforward as
907          * well. Call the appropriate routine and split.
908          */
909         if (aflg) {
910                 printf (_("Adding user %s to group %s\n"), user, group);
911                 grent.gr_mem = add_list (grent.gr_mem, user);
912 #ifdef SHADOWGRP
913                 if (is_shadowgrp) {
914                         sgent.sg_mem = add_list (sgent.sg_mem, user);
915                 }
916 #endif
917 #ifdef WITH_AUDIT
918                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
919                               "adding group member",
920                               user, AUDIT_NO_ID, 1);
921 #endif
922                 SYSLOG ((LOG_INFO, "add member %s to group %s by %s", user,
923                          group, myname));
924                 goto output;
925         }
926
927         /*
928          * Removing a member from the member list is the same deal as adding
929          * one, except the routine is different.
930          */
931         if (dflg) {
932                 bool removed = false;
933
934                 printf (_("Removing user %s from group %s\n"), user, group);
935
936                 if (is_on_list (grent.gr_mem, user)) {
937                         removed = true;
938                         grent.gr_mem = del_list (grent.gr_mem, user);
939                 }
940 #ifdef SHADOWGRP
941                 if (is_shadowgrp) {
942                         if (is_on_list (sgent.sg_mem, user)) {
943                                 removed = true;
944                                 sgent.sg_mem = del_list (sgent.sg_mem, user);
945                         }
946                 }
947 #endif
948                 if (!removed) {
949                         fprintf (stderr, _("%s: user '%s' is not a member of '%s'\n"),
950                                  Prog, user, group);
951 #ifdef WITH_AUDIT
952                         audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
953                                       "deleting member",
954                                       user, AUDIT_NO_ID, 0);
955 #endif
956                         fail_exit (1);
957                 }
958 #ifdef WITH_AUDIT
959                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
960                               "deleting member",
961                               user, AUDIT_NO_ID, 1);
962 #endif
963                 SYSLOG ((LOG_INFO, "remove member %s from group %s by %s",
964                          user, group, myname));
965                 goto output;
966         }
967 #ifdef SHADOWGRP
968         /*
969          * Replacing the entire list of administrators is simple. Check the
970          * list to make sure everyone is a real user. Then slap the new list
971          * in place.
972          */
973         if (Aflg) {
974 #ifdef WITH_AUDIT
975                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
976                               "setting group admin",
977                               group, AUDIT_NO_ID, 1);
978 #endif
979                 SYSLOG ((LOG_INFO, "set administrators of %s to %s",
980                          group, admins));
981                 sgent.sg_adm = comma_to_list (admins);
982                 if (!Mflg) {
983                         goto output;
984                 }
985         }
986 #endif
987
988         /*
989          * Replacing the entire list of members is simple. Check the list to
990          * make sure everyone is a real user. Then slap the new list in
991          * place.
992          */
993         if (Mflg) {
994 #ifdef WITH_AUDIT
995                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
996                               "setting group members",
997                               group, AUDIT_NO_ID, 1);
998 #endif
999                 SYSLOG ((LOG_INFO, "set members of %s to %s", group, members));
1000 #ifdef SHADOWGRP
1001                 sgent.sg_mem = comma_to_list (members);
1002 #endif
1003                 grent.gr_mem = comma_to_list (members);
1004                 goto output;
1005         }
1006
1007         /*
1008          * If the password is being changed, the input and output must both
1009          * be a tty. The typical keyboard signals are caught so the termio
1010          * modes can be restored.
1011          */
1012         if ((isatty (0) == 0) || (isatty (1) == 0)) {
1013                 fprintf (stderr, _("%s: Not a tty\n"), Prog);
1014 #ifdef WITH_AUDIT
1015                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1016                               "changing password",
1017                               group, AUDIT_NO_ID, 0);
1018 #endif
1019                 fail_exit (1);
1020         }
1021
1022         catch_signals (0);      /* save tty modes */
1023
1024         (void) signal (SIGHUP, catch_signals);
1025         (void) signal (SIGINT, catch_signals);
1026         (void) signal (SIGQUIT, catch_signals);
1027         (void) signal (SIGTERM, catch_signals);
1028 #ifdef SIGTSTP
1029         (void) signal (SIGTSTP, catch_signals);
1030 #endif
1031
1032         /* Prompt for the new password */
1033 #ifdef SHADOWGRP
1034         change_passwd (&grent, &sgent);
1035 #else
1036         change_passwd (&grent);
1037 #endif
1038
1039         /*
1040          * This is the common arrival point to output the new group file.
1041          * The freshly crafted entry is in allocated space. The group file
1042          * will be locked and opened for writing. The new entry will be
1043          * output, etc.
1044          */
1045       output:
1046         if (setuid (0) != 0) {
1047                 fputs (_("Cannot change ID to root.\n"), stderr);
1048                 SYSLOG ((LOG_ERR, "can't setuid(0)"));
1049 #ifdef WITH_AUDIT
1050                 audit_logger (AUDIT_USER_CHAUTHTOK, Prog,
1051                               "changing id to root",
1052                               group, AUDIT_NO_ID, 0);
1053 #endif
1054                 closelog ();
1055                 fail_exit (1);
1056         }
1057         pwd_init ();
1058
1059         open_files ();
1060
1061 #ifdef SHADOWGRP
1062         update_group (&grent, &sgent);
1063 #else
1064         update_group (&grent);
1065 #endif
1066
1067         close_files ();
1068
1069         nscd_flush_cache ("group");
1070
1071         exit (E_SUCCESS);
1072 }
1073