]> granicus.if.org Git - shadow/blob - src/groupmems.c
* src/groupmems.c: Remove duplicated gr_open().
[shadow] / src / groupmems.c
1 /*
2  * Copyright (c) 2000       , International Business Machines
3  *                            George Kraft IV, gk4@us.ibm.com, 03/23/2000
4  * Copyright (c) 2000 - 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 #include <fcntl.h>
36 #include <getopt.h>
37 #include <grp.h>
38 #include <stdio.h>
39 #include <sys/types.h>
40 #ifdef USE_PAM
41 #include "pam_defs.h"
42 #endif                          /* USE_PAM */
43 #include <pwd.h>
44 #include "defines.h"
45 #include "prototypes.h"
46 #include "groupio.h"
47 #ifdef SHADOWGRP
48 #include "sgroupio.h"
49 #endif
50
51 /* Exit Status Values */
52
53 #define EXIT_SUCCESS            0       /* success */
54 #define EXIT_USAGE              1       /* invalid command syntax */
55 #define EXIT_GROUP_FILE         2       /* group file access problems */
56 #define EXIT_NOT_ROOT           3       /* not superuser  */
57 #define EXIT_NOT_EROOT          4       /* not effective superuser  */
58 #define EXIT_NOT_PRIMARY        5       /* not primary owner of group  */
59 #define EXIT_NOT_MEMBER         6       /* member of group does not exist */
60 #define EXIT_MEMBER_EXISTS      7       /* member of group already exists */
61 #define EXIT_INVALID_USER       8       /* specified user does not exist */
62 #define EXIT_INVALID_GROUP      9       /* specified group does not exist */
63
64 /*
65  * Global variables
66  */
67 static char *adduser = NULL;
68 static char *deluser = NULL;
69 static char *thisgroup = NULL;
70 static bool purge = false;
71 static bool list = false;
72 static int exclusive = 0;
73 static char *Prog;
74 static bool gr_locked = false;
75 #ifdef SHADOWGRP
76 /* Indicate if shadow groups are enabled on the system
77  * (/etc/gshadow present) */
78 static bool is_shadowgrp;
79 static bool sgr_locked = false;
80 #endif
81
82 /* local function prototypes */
83 static char *whoami (void);
84 static void add_user (const char *user,
85                       const struct group *grp);
86 static void remove_user (const char *user, 
87                          const struct group *grp);
88 static void purge_members (const struct group *grp);
89 static void display_members (const char *const *members);
90 static void usage (void);
91 static void process_flags (int argc, char **argv);
92 static void check_perms (void);
93 static void fail_exit (int code);
94 #define isroot()                (getuid () == 0)
95
96 static char *whoami (void)
97 {
98         /* local, no need for xgetgrgid */
99         struct group *grp = getgrgid (getgid ());
100         /* local, no need for xgetpwuid */
101         struct passwd *usr = getpwuid (getuid ());
102
103         if (   (NULL != usr)
104             && (NULL != grp)
105             && (0 == strcmp (usr->pw_name, grp->gr_name))) {
106                 return xstrdup (usr->pw_name);
107         } else {
108                 return NULL;
109         }
110 }
111
112 /*
113  * add_user - Add an user to the specified group
114  */
115 static void add_user (const char *user,
116                       const struct group *grp)
117 {
118         struct group *newgrp;
119
120         /* Make sure the user is not already part of the group */
121         if (is_on_list (grp->gr_mem, user)) {
122                 fprintf (stderr,
123                          _("%s: user '%s' is already a member of '%s'\n"),
124                          Prog, user, grp->gr_name);
125                 fail_exit (EXIT_MEMBER_EXISTS);
126         }
127
128         newgrp = __gr_dup(grp);
129         if (NULL == newgrp) {
130                 fprintf (stderr,
131                          _("%s: Out of memory. Cannot update %s.\n"),
132                          Prog, gr_dbname ());
133                 fail_exit (13);
134         }
135
136         /* Add the user to the /etc/group group */
137         newgrp->gr_mem = add_list (newgrp->gr_mem, user);
138
139 #ifdef SHADOWGRP
140         if (is_shadowgrp) {
141                 const struct sgrp *sg = sgr_locate (newgrp->gr_name);
142                 struct sgrp *newsg;
143
144                 if (NULL == sg) {
145                         /* Create a shadow group based on this group */
146                         static struct sgrp sgrent;
147                         sgrent.sg_name = xstrdup (newgrp->gr_name);
148                         sgrent.sg_mem = dup_list (newgrp->gr_mem);
149                         sgrent.sg_adm = (char **) xmalloc (sizeof (char *));
150 #ifdef FIRST_MEMBER_IS_ADMIN
151                         if (sgrent.sg_mem[0]) {
152                                 sgrent.sg_adm[0] = xstrdup (sgrent.sg_mem[0]);
153                                 sgrent.sg_adm[1] = NULL;
154                         } else
155 #endif
156                         {
157                                 sgrent.sg_adm[0] = NULL;
158                         }
159
160                         /* Move any password to gshadow */
161                         sgrent.sg_passwd = newgrp->gr_passwd;
162                         newgrp->gr_passwd = SHADOW_PASSWD_STRING;
163
164                         newsg = &sgrent;
165                 } else {
166                         newsg = __sgr_dup (sg);
167                         if (NULL == newsg) {
168                                 fprintf (stderr,
169                                          _("%s: Out of memory. Cannot update %s.\n"),
170                                          Prog, sgr_dbname ());
171                                 fail_exit (13);
172                         }
173                         /* Add the user to the members */
174                         newsg->sg_mem = add_list (newsg->sg_mem, user);
175                         /* Do not touch the administrators */
176                 }
177
178                 if (sgr_update (newsg) == 0) {
179                         fprintf (stderr,
180                                  _("%s: failed to prepare the new %s entry '%s'\n"),
181                                  Prog, sgr_dbname (), newsg->sg_name);
182                         fail_exit (13);
183                 }
184         }
185 #endif
186
187         if (gr_update (newgrp) == 0) {
188                 fprintf (stderr,
189                          _("%s: failed to prepare the new %s entry '%s'\n"),
190                          Prog, gr_dbname (), newgrp->gr_name);
191                 fail_exit (13);
192         }
193 }
194
195 /*
196  * remove_user - Remove an user from a given group
197  */
198 static void remove_user (const char *user, 
199                          const struct group *grp)
200 {
201         struct group *newgrp;
202
203         /* Check if the user is a member of the specified group */
204         if (!is_on_list (grp->gr_mem, user)) {
205                 fprintf (stderr,
206                          _("%s: user '%s' is not a member of '%s'\n"),
207                          Prog, user, grp->gr_name);
208                 fail_exit (EXIT_NOT_MEMBER);
209         }
210
211         newgrp = __gr_dup (grp);
212         if (NULL == newgrp) {
213                 fprintf (stderr,
214                          _("%s: Out of memory. Cannot update %s.\n"),
215                          Prog, gr_dbname ());
216                 fail_exit (13);
217         }
218
219         /* Remove the user from the /etc/group group */
220         newgrp->gr_mem = del_list (newgrp->gr_mem, user);
221
222 #ifdef SHADOWGRP
223         if (is_shadowgrp) {
224                 const struct sgrp *sg = sgr_locate (newgrp->gr_name);
225                 struct sgrp *newsg;
226
227                 if (NULL == sg) {
228                         /* Create a shadow group based on this group */
229                         static struct sgrp sgrent;
230                         sgrent.sg_name = xstrdup (newgrp->gr_name);
231                         sgrent.sg_mem = dup_list (newgrp->gr_mem);
232                         sgrent.sg_adm = (char **) xmalloc (sizeof (char *));
233 #ifdef FIRST_MEMBER_IS_ADMIN
234                         if (sgrent.sg_mem[0]) {
235                                 sgrent.sg_adm[0] = xstrdup (sgrent.sg_mem[0]);
236                                 sgrent.sg_adm[1] = NULL;
237                         } else
238 #endif
239                         {
240                                 sgrent.sg_adm[0] = NULL;
241                         }
242
243                         /* Move any password to gshadow */
244                         sgrent.sg_passwd = newgrp->gr_passwd;
245                         newgrp->gr_passwd = SHADOW_PASSWD_STRING;
246
247                         newsg = &sgrent;
248                 } else {
249                         newsg = __sgr_dup (sg);
250                         if (NULL == newsg) {
251                                 fprintf (stderr,
252                                          _("%s: Out of memory. Cannot update %s.\n"),
253                                          Prog, sgr_dbname ());
254                                 fail_exit (13);
255                         }
256                         /* Remove the user from the members */
257                         newsg->sg_mem = del_list (newsg->sg_mem, user);
258                         /* Remove the user from the administrators */
259                         newsg->sg_adm = del_list (newsg->sg_adm, user);
260                 }
261
262                 if (sgr_update (newsg) == 0) {
263                         fprintf (stderr,
264                                  _("%s: failed to prepare the new %s entry '%s'\n"),
265                                  Prog, sgr_dbname (), newsg->sg_name);
266                         fail_exit (13);
267                 }
268         }
269 #endif
270
271         if (gr_update (newgrp) == 0) {
272                 fprintf (stderr,
273                          _("%s: failed to prepare the new %s entry '%s'\n"),
274                          Prog, gr_dbname (), newgrp->gr_name);
275                 fail_exit (13);
276         }
277 }
278
279 /*
280  * purge_members - Rmeove every members of the specified group
281  */
282 static void purge_members (const struct group *grp)
283 {
284         struct group *newgrp = __gr_dup (grp);
285
286         if (NULL == newgrp) {
287                 fprintf (stderr,
288                          _("%s: Out of memory. Cannot update %s.\n"),
289                          Prog, gr_dbname ());
290                 fail_exit (13);
291         }
292
293         /* Remove all the members of the /etc/group group */
294         newgrp->gr_mem[0] = NULL;
295
296 #ifdef SHADOWGRP
297         if (is_shadowgrp) {
298                 const struct sgrp *sg = sgr_locate (newgrp->gr_name);
299                 struct sgrp *newsg;
300
301                 if (NULL == sg) {
302                         /* Create a shadow group based on this group */
303                         static struct sgrp sgrent;
304                         sgrent.sg_name = xstrdup (newgrp->gr_name);
305                         sgrent.sg_mem = (char **) xmalloc (sizeof (char *));
306                         sgrent.sg_mem[0] = NULL;
307                         sgrent.sg_adm = (char **) xmalloc (sizeof (char *));
308                         sgrent.sg_adm[0] = NULL;
309
310                         /* Move any password to gshadow */
311                         sgrent.sg_passwd = newgrp->gr_passwd;
312                         newgrp->gr_passwd = xstrdup(SHADOW_PASSWD_STRING);
313
314                         newsg = &sgrent;
315                 } else {
316                         newsg = __sgr_dup (sg);
317                         if (NULL == newsg) {
318                                 fprintf (stderr,
319                                          _("%s: Out of memory. Cannot update %s.\n"),
320                                          Prog, sgr_dbname ());
321                                 fail_exit (13);
322                         }
323                         /* Remove all the members of the /etc/gshadow
324                          * group */
325                         newsg->sg_mem[0] = NULL;
326                         /* Remove all the administrators of the
327                          * /etc/gshadow group */
328                         newsg->sg_adm[0] = NULL;
329                 }
330
331                 if (sgr_update (newsg) == 0) {
332                         fprintf (stderr,
333                                  _("%s: failed to prepare the new %s entry '%s'\n"),
334                                  Prog, sgr_dbname (), newsg->sg_name);
335                         fail_exit (13);
336                 }
337         }
338 #endif
339
340         if (gr_update (newgrp) == 0) {
341                 fprintf (stderr,
342                          _("%s: failed to prepare the new %s entry '%s'\n"),
343                          Prog, gr_dbname (), newgrp->gr_name);
344                 fail_exit (13);
345         }
346 }
347
348 static void display_members (const char *const *members)
349 {
350         int i;
351
352         for (i = 0; NULL != members[i]; i++) {
353                 printf ("%s ", members[i]);
354
355                 if (NULL == members[i + 1]) {
356                         printf ("\n");
357                 } else {
358                         printf (" ");
359                 }
360         }
361 }
362
363 static void usage (void)
364 {
365         (void) fputs (_("Usage: groupmems -a username | -d username | -p | -l [-g groupname]\n"), stderr);
366         fail_exit (EXIT_USAGE);
367 }
368
369 /*
370  * process_flags - perform command line argument setting
371  */
372 static void process_flags (int argc, char **argv)
373 {
374         int arg;
375         int option_index = 0;
376         static struct option long_options[] = {
377                 {"add", required_argument, NULL, 'a'},
378                 {"delete", required_argument, NULL, 'd'},
379                 {"group", required_argument, NULL, 'g'},
380                 {"list", no_argument, NULL, 'l'},
381                 {"purge", no_argument, NULL, 'p'},
382                 {NULL, 0, NULL, '\0'}
383         };
384
385         while ((arg = getopt_long (argc, argv, "a:d:g:lp", long_options,
386                                    &option_index)) != EOF) {
387                 switch (arg) {
388                 case 'a':
389                         adduser = xstrdup (optarg);
390                         ++exclusive;
391                         break;
392                 case 'd':
393                         deluser = xstrdup (optarg);
394                         ++exclusive;
395                         break;
396                 case 'g':
397                         thisgroup = xstrdup (optarg);
398                         break;
399                 case 'l':
400                         list = true;
401                         ++exclusive;
402                         break;
403                 case 'p':
404                         purge = true;
405                         ++exclusive;
406                         break;
407                 default:
408                         usage ();
409                 }
410         }
411
412         if ((exclusive > 1) || (optind < argc)) {
413                 usage ();
414         }
415
416         /* local, no need for xgetpwnam */
417         if (   (NULL != adduser)
418             && (getpwnam (adduser) == NULL)) {
419                 fprintf (stderr, _("%s: user '%s' does not exist\n"),
420                          Prog, adduser);
421                 fail_exit (EXIT_INVALID_USER);
422         }
423
424 }
425
426 static void check_perms (void)
427 {
428         if (!list) {
429 #ifdef USE_PAM
430                 pam_handle_t *pamh = NULL;
431                 int retval = PAM_SUCCESS;
432                 struct passwd *pampw;
433
434                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
435                 if (NULL == pampw) {
436                         retval = PAM_USER_UNKNOWN;
437                 } else {
438                         retval = pam_start ("groupmems", pampw->pw_name,
439                                             &conv, &pamh);
440                 }
441
442                 if (PAM_SUCCESS == retval) {
443                         retval = pam_authenticate (pamh, 0);
444                 }
445
446                 if (PAM_SUCCESS == retval) {
447                         retval = pam_acct_mgmt (pamh, 0);
448                 }
449
450                 (void) pam_end (pamh, retval);
451                 if (PAM_SUCCESS != retval) {
452                         fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
453                         fail_exit (1);
454                 }
455 #endif
456         }
457 }
458
459 static void fail_exit (int code)
460 {
461         if (gr_locked) {
462                 if (gr_unlock () == 0) {
463                         fprintf (stderr,
464                                  _("%s: failed to unlock %s\n"),
465                                  Prog, gr_dbname ());
466                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
467                         /* continue */
468                 }
469         }
470
471 #ifdef SHADOWGRP
472         if (sgr_locked) {
473                 if (sgr_unlock () == 0) {
474                         fprintf (stderr,
475                                  _("%s: failed to unlock %s\n"),
476                                  Prog, sgr_dbname ());
477                         SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
478                         /* continue */
479                 }
480         }
481 #endif
482
483         exit (code);
484 }
485
486 static void open_files (void)
487 {
488         if (!list) {
489                 if (gr_lock () == 0) {
490                         fprintf (stderr,
491                                  _("%s: cannot lock %s; try again later.\n"),
492                                  Prog, gr_dbname ());
493                         fail_exit (EXIT_GROUP_FILE);
494                 }
495                 gr_locked = true;
496
497 #ifdef SHADOWGRP
498                 if (is_shadowgrp) {
499                         if (sgr_lock () == 0) {
500                                 fprintf (stderr,
501                                          _("%s: cannot lock %s; try again later.\n"),
502                                          Prog, sgr_dbname ());
503                                 fail_exit (EXIT_GROUP_FILE);
504                         }
505                         sgr_locked = true;
506                 }
507 #endif
508         }
509
510         if (gr_open (list ? O_RDONLY : O_RDWR) == 0) {
511                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
512                 fail_exit (EXIT_GROUP_FILE);
513         }
514
515 #ifdef SHADOWGRP
516         if (is_shadowgrp) {
517                 if (sgr_open (list ? O_RDONLY : O_RDWR) == 0) {
518                         fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ());
519                         fail_exit (EXIT_GROUP_FILE);
520                 }
521         }
522 #endif
523 }
524
525 static void close_files (void)
526 {
527         if ((gr_close () == 0) && !list) {
528                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
529                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
530                 fail_exit (EXIT_GROUP_FILE);
531         }
532         if (gr_locked) {
533                 if (gr_unlock () == 0) {
534                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
535                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
536                         /* continue */
537                 }
538                 gr_locked = false;
539         }
540
541 #ifdef SHADOWGRP
542         if (is_shadowgrp) {
543                 if ((sgr_close () == 0) && !list) {
544                         fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
545                         SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
546                         fail_exit (EXIT_GROUP_FILE);
547                 }
548                 if (sgr_locked) {
549                         if (sgr_unlock () == 0) {
550                                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
551                                 SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
552                                 /* continue */
553                         }
554                         sgr_locked = false;
555                 }
556         }
557 #endif
558 }
559
560 int main (int argc, char **argv) 
561 {
562         char *name;
563         const struct group *grp;
564
565         /*
566          * Get my name so that I can use it to report errors.
567          */
568         Prog = Basename (argv[0]);
569
570         OPENLOG ("groupmems");
571
572         (void) setlocale (LC_ALL, "");
573         (void) bindtextdomain (PACKAGE, LOCALEDIR);
574         (void) textdomain (PACKAGE);
575
576 #ifdef SHADOWGRP
577         is_shadowgrp = sgr_file_present ();
578 #endif
579
580         process_flags (argc, argv);
581
582         if (NULL == thisgroup) {
583                 name = whoami ();
584                 if (!list && (NULL == name)) {
585                         fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog);
586                         fail_exit (EXIT_NOT_PRIMARY);
587                 }
588         } else {
589                 name = thisgroup;
590                 if (!list && !isroot ()) {
591                         fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog);
592                         fail_exit (EXIT_NOT_ROOT);
593                 }
594         }
595
596         if (!list) {
597                 check_perms ();
598
599                 if (gr_lock () == 0) {
600                         fprintf (stderr,
601                                  _("%s: cannot lock %s; try again later.\n"),
602                                  Prog, gr_dbname ());
603                         fail_exit (EXIT_GROUP_FILE);
604                 }
605                 gr_locked = true;
606         }
607
608         grp = gr_locate (name);
609
610         if (NULL == grp) {
611                 fprintf (stderr, _("%s: group '%s' does not exist in %s\n"),
612                          Prog, name, gr_dbname ());
613                 fail_exit (EXIT_INVALID_GROUP);
614         }
615
616         if (list) {
617                 display_members ((const char *const *)grp->gr_mem);
618         } else if (NULL != adduser) {
619                 add_user (adduser, grp);
620         } else if (NULL != deluser) {
621                 remove_user (deluser, grp);
622         } else if (purge) {
623                 purge_members (grp);
624         }
625
626         if (gr_close () == 0) {
627                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
628                 SYSLOG ((LOG_ERR, "failure while writing %s", gr_dbname ()));
629                 fail_exit (EXIT_GROUP_FILE);
630         }
631         if (gr_unlock () == 0) {
632                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
633                 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
634                 /* continue */
635         }
636
637         exit (EXIT_SUCCESS);
638 }
639