]> granicus.if.org Git - shadow/blob - src/groupmems.c
* src/groupmems.c: The grp structure returned by gr_locate is a
[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
48 /* Exit Status Values */
49
50 #define EXIT_SUCCESS            0       /* success */
51 #define EXIT_USAGE              1       /* invalid command syntax */
52 #define EXIT_GROUP_FILE         2       /* group file access problems */
53 #define EXIT_NOT_ROOT           3       /* not superuser  */
54 #define EXIT_NOT_EROOT          4       /* not effective superuser  */
55 #define EXIT_NOT_PRIMARY        5       /* not primary owner of group  */
56 #define EXIT_NOT_MEMBER         6       /* member of group does not exist */
57 #define EXIT_MEMBER_EXISTS      7       /* member of group already exists */
58 #define EXIT_INVALID_USER       8       /* specified user does not exist */
59 #define EXIT_INVALID_GROUP      9       /* specified group does not exist */
60
61 /*
62  * Global variables
63  */
64 static char *adduser = NULL;
65 static char *deluser = NULL;
66 static char *thisgroup = NULL;
67 static bool purge = false;
68 static bool list = false;
69 static int exclusive = 0;
70 static char *Prog;
71 static bool gr_locked = false;
72
73 /* local function prototypes */
74 static char *whoami (void);
75 static void add_user (const char *user,
76                       const struct group *grp);
77 static void remove_user (const char *user, 
78                          const struct group *grp);
79 static void purge_members (const struct group *grp);
80 static void display_members (const char *const *members);
81 static void usage (void);
82 static void process_flags (int argc, char **argv);
83 static void check_perms (void);
84 static void fail_exit (int code);
85 #define isroot()                (getuid () == 0)
86
87 static char *whoami (void)
88 {
89         /* local, no need for xgetgrgid */
90         struct group *grp = getgrgid (getgid ());
91         /* local, no need for xgetpwuid */
92         struct passwd *usr = getpwuid (getuid ());
93
94         if (   (NULL != usr)
95             && (NULL != grp)
96             && (0 == strcmp (usr->pw_name, grp->gr_name))) {
97                 return xstrdup (usr->pw_name);
98         } else {
99                 return NULL;
100         }
101 }
102
103 /*
104  * add_user - Add an user to the specified group
105  */
106 static void add_user (const char *user,
107                       const struct group *grp)
108 {
109         struct group *newgrp;
110
111         /* Make sure the user is not already part of the group */
112         if (is_on_list (grp->gr_mem, user)) {
113                 fprintf (stderr,
114                          _("%s: user '%s' is already a member of '%s'\n"),
115                          Prog, user, grp->gr_name);
116                 fail_exit (EXIT_MEMBER_EXISTS);
117         }
118
119         newgrp = __gr_dup(grp);
120         if (NULL == newgrp) {
121                 fprintf (stderr,
122                          _("%s: Out of memory. Cannot update %s.\n"),
123                          Prog, gr_dbname ());
124                 exit (13);
125         }
126
127         /* Add the user to the /etc/group group */
128         newgrp->gr_mem = add_list (newgrp->gr_mem, user);
129         if (gr_update (newgrp) == 0) {
130                 fprintf (stderr,
131                          _("%s: failed to prepare the new %s entry '%s'\n"),
132                          Prog, gr_dbname (), newgrp->gr_name);
133                 fail_exit (13);
134         }
135 }
136
137 /*
138  * remove_user - Remove an user from a given group
139  */
140 static void remove_user (const char *user, 
141                          const struct group *grp)
142 {
143         struct group *newgrp;
144
145         /* Check if the user is a member of the specified group */
146         if (!is_on_list (grp->gr_mem, user)) {
147                 fprintf (stderr,
148                          _("%s: user '%s' is not a member of '%s'\n"),
149                          Prog, user, grp->gr_name);
150                 fail_exit (EXIT_NOT_MEMBER);
151         }
152
153         newgrp = __gr_dup (grp);
154         if (NULL == newgrp) {
155                 fprintf (stderr,
156                          _("%s: Out of memory. Cannot update %s.\n"),
157                          Prog, gr_dbname ());
158                 exit (13);
159         }
160
161         /* Remove the user from the /etc/group group */
162         newgrp->gr_mem = del_list (newgrp->gr_mem, user);
163         if (gr_update (newgrp) == 0) {
164                 fprintf (stderr,
165                          _("%s: failed to prepare the new %s entry '%s'\n"),
166                          Prog, gr_dbname (), newgrp->gr_name);
167                 fail_exit (13);
168         }
169 }
170
171 /*
172  * purge_members - Rmeove every members of the specified group
173  */
174 static void purge_members (const struct group *grp)
175 {
176         struct group *newgrp = __gr_dup (grp);
177
178         if (NULL == newgrp) {
179                 fprintf (stderr,
180                          _("%s: Out of memory. Cannot update %s.\n"),
181                          Prog, gr_dbname ());
182                 exit (13);
183         }
184
185         /* Remove all the members of the /etc/group group */
186         newgrp->gr_mem[0] = NULL;
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 static void display_members (const char *const *members)
196 {
197         int i;
198
199         for (i = 0; NULL != members[i]; i++) {
200                 printf ("%s ", members[i]);
201
202                 if (NULL == members[i + 1]) {
203                         printf ("\n");
204                 } else {
205                         printf (" ");
206                 }
207         }
208 }
209
210 static void usage (void)
211 {
212         (void) fputs (_("Usage: groupmems -a username | -d username | -p | -l [-g groupname]\n"), stderr);
213         fail_exit (EXIT_USAGE);
214 }
215
216 /*
217  * process_flags - perform command line argument setting
218  */
219 static void process_flags (int argc, char **argv)
220 {
221         int arg;
222         int option_index = 0;
223         static struct option long_options[] = {
224                 {"add", required_argument, NULL, 'a'},
225                 {"delete", required_argument, NULL, 'd'},
226                 {"group", required_argument, NULL, 'g'},
227                 {"list", no_argument, NULL, 'l'},
228                 {"purge", no_argument, NULL, 'p'},
229                 {NULL, 0, NULL, '\0'}
230         };
231
232         while ((arg = getopt_long (argc, argv, "a:d:g:lp", long_options,
233                                    &option_index)) != EOF) {
234                 switch (arg) {
235                 case 'a':
236                         adduser = xstrdup (optarg);
237                         ++exclusive;
238                         break;
239                 case 'd':
240                         deluser = xstrdup (optarg);
241                         ++exclusive;
242                         break;
243                 case 'g':
244                         thisgroup = xstrdup (optarg);
245                         break;
246                 case 'l':
247                         list = true;
248                         ++exclusive;
249                         break;
250                 case 'p':
251                         purge = true;
252                         ++exclusive;
253                         break;
254                 default:
255                         usage ();
256                 }
257         }
258
259         if ((exclusive > 1) || (optind < argc)) {
260                 usage ();
261         }
262
263         /* local, no need for xgetpwnam */
264         if (   (NULL != adduser)
265             && (getpwnam (adduser) == NULL)) {
266                 fprintf (stderr, _("%s: user '%s' does not exist\n"),
267                          Prog, adduser);
268                 fail_exit (EXIT_INVALID_USER);
269         }
270
271 }
272
273 static void check_perms (void)
274 {
275 #ifdef USE_PAM
276         pam_handle_t *pamh = NULL;
277         int retval = PAM_SUCCESS;
278         struct passwd *pampw;
279
280         pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
281         if (NULL == pampw) {
282                 retval = PAM_USER_UNKNOWN;
283         } else {
284                 retval = pam_start ("groupmems", pampw->pw_name,
285                                     &conv, &pamh);
286         }
287
288         if (PAM_SUCCESS == retval) {
289                 retval = pam_authenticate (pamh, 0);
290         }
291
292         if (PAM_SUCCESS == retval) {
293                 retval = pam_acct_mgmt (pamh, 0);
294         }
295
296         (void) pam_end (pamh, retval);
297         if (PAM_SUCCESS != retval) {
298                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
299                 fail_exit (1);
300         }
301 #endif
302 }
303
304 static void fail_exit (int code)
305 {
306         if (gr_locked) {
307                 if (gr_unlock () == 0) {
308                         fprintf (stderr,
309                                  _("%s: failed to unlock %s\n"),
310                                  Prog, gr_dbname ());
311                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
312                         /* continue */
313                 }
314         }
315         exit (code);
316 }
317
318 static void open_files (void)
319 {
320         if (!list) {
321                 if (gr_lock () == 0) {
322                         fprintf (stderr,
323                                  _("%s: cannot lock %s; try again later.\n"),
324                                  Prog, gr_dbname ());
325                         fail_exit (EXIT_GROUP_FILE);
326                 }
327                 gr_locked = true;
328         }
329
330         if (gr_open (list ? O_RDONLY : O_RDWR) == 0) {
331                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
332                 fail_exit (EXIT_GROUP_FILE);
333         }
334 }
335
336 static void close_files (void)
337 {
338         if ((gr_close () == 0) && !list) {
339                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
340                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
341                 fail_exit (EXIT_GROUP_FILE);
342         }
343         if (gr_locked) {
344                 if (gr_unlock () == 0) {
345                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
346                         SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
347                         /* continue */
348                 }
349                 gr_locked = false;
350         }
351 }
352
353 int main (int argc, char **argv) 
354 {
355         char *name;
356         const struct group *grp;
357
358         /*
359          * Get my name so that I can use it to report errors.
360          */
361         Prog = Basename (argv[0]);
362
363         OPENLOG ("groupmems");
364
365         (void) setlocale (LC_ALL, "");
366         (void) bindtextdomain (PACKAGE, LOCALEDIR);
367         (void) textdomain (PACKAGE);
368
369         process_flags (argc, argv);
370
371         if (NULL == thisgroup) {
372                 name = whoami ();
373                 if (!list && (NULL == name)) {
374                         fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog);
375                         fail_exit (EXIT_NOT_PRIMARY);
376                 }
377         } else {
378                 name = thisgroup;
379                 if (!list && !isroot ()) {
380                         fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog);
381                         fail_exit (EXIT_NOT_ROOT);
382                 }
383         }
384
385         if (!list) {
386                 check_perms ();
387
388                 if (gr_lock () == 0) {
389                         fprintf (stderr,
390                                  _("%s: cannot lock %s; try again later.\n"),
391                                  Prog, gr_dbname ());
392                         fail_exit (EXIT_GROUP_FILE);
393                 }
394                 gr_locked = true;
395         }
396
397         if (gr_open (list ? O_RDONLY : O_RDWR) == 0) {
398                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
399                 fail_exit (EXIT_GROUP_FILE);
400         }
401
402         grp = gr_locate (name);
403
404         if (NULL == grp) {
405                 fprintf (stderr, _("%s: group '%s' does not exist in %s\n"),
406                          Prog, name, gr_dbname ());
407                 fail_exit (EXIT_INVALID_GROUP);
408         }
409
410         if (list) {
411                 display_members ((const char *const *)grp->gr_mem);
412         } else if (NULL != adduser) {
413                 add_user (adduser, grp);
414         } else if (NULL != deluser) {
415                 remove_user (deluser, grp);
416         } else if (purge) {
417                 purge_members (grp);
418         }
419
420         if (gr_close () == 0) {
421                 fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
422                 SYSLOG ((LOG_ERR, "failure while writing %s", gr_dbname ()));
423                 fail_exit (EXIT_GROUP_FILE);
424         }
425         if (gr_unlock () == 0) {
426                 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
427                 SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
428                 /* continue */
429         }
430
431         exit (EXIT_SUCCESS);
432 }
433