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