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