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