]> granicus.if.org Git - shadow/blob - src/groupdel.c
* src/chage.c, src/chfn.c, src/chgpasswd.c, src/chpasswd.c,
[shadow] / src / groupdel.c
1 /*
2  * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5  * Copyright (c) 2007 - 2011, 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 <ctype.h>
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #ifdef ACCT_TOOLS_SETUID
42 #ifdef USE_PAM
43 #include "pam_defs.h"
44 #endif                          /* USE_PAM */
45 #endif                          /* ACCT_TOOLS_SETUID */
46 #include <stdio.h>
47 #include <sys/types.h>
48 #include <getopt.h>
49 #include "defines.h"
50 #include "groupio.h"
51 #include "nscd.h"
52 #include "prototypes.h"
53 #ifdef  SHADOWGRP
54 #include "sgroupio.h"
55 #endif
56 /*
57  * Global variables
58  */
59 const char *Prog;
60
61 static char *group_name;
62 static gid_t group_id = -1;
63
64 #ifdef  SHADOWGRP
65 static bool is_shadow_grp;
66 #endif
67
68 /*
69  * exit status values
70  */
71 /*@-exitarg@*/
72 #define E_SUCCESS       0       /* success */
73 #define E_USAGE         2       /* invalid command syntax */
74 #define E_NOTFOUND      6       /* specified group doesn't exist */
75 #define E_GROUP_BUSY    8       /* can't remove user's primary group */
76 #define E_GRP_UPDATE    10      /* can't update group file */
77
78 /* local function prototypes */
79 static /*@noreturn@*/void usage (int status);
80 static void grp_update (void);
81 static void close_files (void);
82 static void open_files (void);
83 static void group_busy (gid_t gid);
84 static void process_flags (int argc, char **argv);
85
86 /*
87  * usage - display usage message and exit
88  */
89 static /*@noreturn@*/void usage (int status)
90 {
91         FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
92         (void) fprintf (usageout,
93                         _("Usage: %s [options] GROUP\n"
94                           "\n"
95                           "Options:\n"),
96                         Prog);
97         (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
98         (void) fputs (_("  -R, --root CHROOT_DIR         directory to chroot into\n"), usageout);
99         (void) fputs ("\n", usageout);
100         exit (status);
101 }
102
103 /*
104  * grp_update - update group file entries
105  *
106  *      grp_update() writes the new records to the group files.
107  */
108 static void grp_update (void)
109 {
110         /*
111          * To add the group, we need to update /etc/group.
112          * Make sure failures will be reported.
113          */
114         add_cleanup (cleanup_report_del_group_group, group_name);
115 #ifdef  SHADOWGRP
116         if (is_shadow_grp) {
117                 /* We also need to update /etc/gshadow */
118                 add_cleanup (cleanup_report_del_group_gshadow, group_name);
119         }
120 #endif
121
122         /*
123          * Delete the group entry.
124          */
125         if (gr_remove (group_name) == 0) {
126                 fprintf (stderr,
127                          _("%s: cannot remove entry '%s' from %s\n"),
128                          Prog, group_name, gr_dbname ());
129                 exit (E_GRP_UPDATE);
130         }
131
132 #ifdef  SHADOWGRP
133         /*
134          * Delete the shadow group entries as well.
135          */
136         if (is_shadow_grp && (sgr_locate (group_name) != NULL)) {
137                 if (sgr_remove (group_name) == 0) {
138                         fprintf (stderr,
139                                  _("%s: cannot remove entry '%s' from %s\n"),
140                                  Prog, group_name, sgr_dbname ());
141                         exit (E_GRP_UPDATE);
142                 }
143         }
144 #endif                          /* SHADOWGRP */
145 }
146
147 /*
148  * close_files - close all of the files that were opened
149  *
150  *      close_files() closes all of the files that were opened for this
151  *      new group.  This causes any modified entries to be written out.
152  */
153 static void close_files (void)
154 {
155         /* First, write the changes in the regular group database */
156         if (gr_close () == 0) {
157                 fprintf (stderr,
158                          _("%s: failure while writing changes to %s\n"),
159                          Prog, gr_dbname ());
160                 exit (E_GRP_UPDATE);
161         }
162
163 #ifdef WITH_AUDIT
164         audit_logger (AUDIT_DEL_GROUP, Prog,
165                       "removing group from /etc/group",
166                       group_name, (unsigned int) group_id,
167                       SHADOW_AUDIT_SUCCESS);
168 #endif
169         SYSLOG ((LOG_INFO,
170                  "group '%s' removed from %s",
171                  group_name, gr_dbname ()));
172         del_cleanup (cleanup_report_del_group_group);
173
174         cleanup_unlock_group (NULL);
175         del_cleanup (cleanup_unlock_group);
176
177
178         /* Then, write the changes in the shadow database */
179 #ifdef  SHADOWGRP
180         if (is_shadow_grp) {
181                 if (sgr_close () == 0) {
182                         fprintf (stderr,
183                                  _("%s: failure while writing changes to %s\n"),
184                                  Prog, sgr_dbname ());
185                         exit (E_GRP_UPDATE);
186                 }
187
188 #ifdef WITH_AUDIT
189                 audit_logger (AUDIT_DEL_GROUP, Prog,
190                               "removing group from /etc/gshadow",
191                               group_name, (unsigned int) group_id,
192                               SHADOW_AUDIT_SUCCESS);
193 #endif
194                 SYSLOG ((LOG_INFO,
195                          "group '%s' removed from %s",
196                          group_name, sgr_dbname ()));
197                 del_cleanup (cleanup_report_del_group_gshadow);
198
199                 cleanup_unlock_gshadow (NULL);
200                 del_cleanup (cleanup_unlock_gshadow);
201         }
202 #endif                          /* SHADOWGRP */
203
204         /* Report success at the system level */
205 #ifdef WITH_AUDIT
206         audit_logger (AUDIT_DEL_GROUP, Prog,
207                       "",
208                       group_name, (unsigned int) group_id,
209                       SHADOW_AUDIT_SUCCESS);
210 #endif
211         SYSLOG ((LOG_INFO, "group '%s' removed\n", group_name));
212         del_cleanup (cleanup_report_del_group);
213 }
214
215 /*
216  * open_files - lock and open the group files
217  *
218  *      open_files() opens the two group files.
219  */
220 static void open_files (void)
221 {
222         /* First, lock the databases */
223         if (gr_lock () == 0) {
224                 fprintf (stderr,
225                          _("%s: cannot lock %s; try again later.\n"),
226                          Prog, gr_dbname ());
227                 exit (E_GRP_UPDATE);
228         }
229         add_cleanup (cleanup_unlock_group, NULL);
230 #ifdef  SHADOWGRP
231         if (is_shadow_grp) {
232                 if (sgr_lock () == 0) {
233                         fprintf (stderr,
234                                  _("%s: cannot lock %s; try again later.\n"),
235                                  Prog, sgr_dbname ());
236                         exit (E_GRP_UPDATE);
237                 }
238                 add_cleanup (cleanup_unlock_gshadow, NULL);
239         }
240 #endif
241
242         /*
243          * Now, if the group is not removed, it's our fault.
244          * Make sure failures will be reported.
245          */
246         add_cleanup (cleanup_report_del_group, group_name);
247
248         /* An now open the databases */
249         if (gr_open (O_RDWR) == 0) {
250                 fprintf (stderr,
251                          _("%s: cannot open %s\n"),
252                          Prog, gr_dbname ());
253                 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
254                 exit (E_GRP_UPDATE);
255         }
256 #ifdef  SHADOWGRP
257         if (is_shadow_grp) {
258                 if (sgr_open (O_RDWR) == 0) {
259                         fprintf (stderr,
260                                  _("%s: cannot open %s\n"),
261                                  Prog, sgr_dbname ());
262                         SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
263                         exit (E_GRP_UPDATE);
264                 }
265         }
266 #endif                          /* SHADOWGRP */
267 }
268
269 /*
270  * group_busy - check if this is any user's primary group
271  *
272  *      group_busy verifies that this group is not the primary group
273  *      for any user.  You must remove all users before you remove
274  *      the group.
275  */
276 static void group_busy (gid_t gid)
277 {
278         struct passwd *pwd;
279
280         /*
281          * Nice slow linear search.
282          */
283
284         setpwent ();
285
286         while ( ((pwd = getpwent ()) != NULL) && (pwd->pw_gid != gid) );
287
288         endpwent ();
289
290         /*
291          * If pwd isn't NULL, it stopped because the gid's matched.
292          */
293
294         if (pwd == (struct passwd *) 0) {
295                 return;
296         }
297
298         /*
299          * Can't remove the group.
300          */
301         fprintf (stderr,
302                  _("%s: cannot remove the primary group of user '%s'\n"),
303                  Prog, pwd->pw_name);
304         exit (E_GROUP_BUSY);
305 }
306
307 /*
308  * process_flags - parse the command line options
309  *
310  *      It will not return if an error is encountered.
311  */
312 static void process_flags (int argc, char **argv)
313 {
314         /*
315          * Parse the command line options.
316          */
317         int c;
318         static struct option long_options[] = {
319                 {"help", no_argument,       NULL, 'h'},
320                 {"root", required_argument, NULL, 'R'},
321                 {NULL, 0, NULL, '\0'}
322         };
323
324         while ((c = getopt_long (argc, argv, "hR:",
325                                  long_options, NULL)) != -1) {
326                 switch (c) {
327                 case 'h':
328                         usage (E_SUCCESS);
329                         /*@notreached@*/break;
330                 case 'R': /* no-op, handled in process_root_flag () */
331                         break;
332                 default:
333                         usage (E_USAGE);
334                 }
335         }
336
337         if (optind != argc - 1) {
338                 usage (E_USAGE);
339         }
340         group_name = argv[optind];
341 }
342
343 /*
344  * main - groupdel command
345  *
346  *      The syntax of the groupdel command is
347  *      
348  *      groupdel group
349  *
350  *      The named group will be deleted.
351  */
352
353 int main (int argc, char **argv)
354 {
355 #ifdef ACCT_TOOLS_SETUID
356 #ifdef USE_PAM
357         pam_handle_t *pamh = NULL;
358         int retval;
359 #endif                          /* USE_PAM */
360 #endif                          /* ACCT_TOOLS_SETUID */
361
362         /*
363          * Get my name so that I can use it to report errors.
364          */
365         Prog = Basename (argv[0]);
366
367         (void) setlocale (LC_ALL, "");
368         (void) bindtextdomain (PACKAGE, LOCALEDIR);
369         (void) textdomain (PACKAGE);
370
371         process_root_flag ("-R", argc, argv);
372
373         OPENLOG ("groupdel");
374 #ifdef WITH_AUDIT
375         audit_help_open ();
376 #endif
377
378         if (atexit (do_cleanups) != 0) {
379                 fprintf (stderr,
380                          _("%s: Cannot setup cleanup service.\n"),
381                          Prog);
382                 exit (1);
383         }
384
385         process_flags (argc, argv);
386
387 #ifdef ACCT_TOOLS_SETUID
388 #ifdef USE_PAM
389         {
390                 struct passwd *pampw;
391                 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
392                 if (pampw == NULL) {
393                         fprintf (stderr,
394                                  _("%s: Cannot determine your user name.\n"),
395                                  Prog);
396                         exit (1);
397                 }
398
399                 retval = pam_start ("groupdel", pampw->pw_name, &conv, &pamh);
400         }
401
402         if (PAM_SUCCESS == retval) {
403                 retval = pam_authenticate (pamh, 0);
404         }
405
406         if (PAM_SUCCESS == retval) {
407                 retval = pam_acct_mgmt (pamh, 0);
408         }
409
410         if (PAM_SUCCESS != retval) {
411                 fprintf (stderr, _("%s: PAM: %s\n"),
412                          Prog, pam_strerror (pamh, retval));
413                 SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
414                 if (NULL != pamh) {
415                         (void) pam_end (pamh, retval);
416                 }
417                 exit (1);
418         }
419         (void) pam_end (pamh, retval);
420 #endif                          /* USE_PAM */
421 #endif                          /* ACCT_TOOLS_SETUID */
422
423 #ifdef SHADOWGRP
424         is_shadow_grp = sgr_file_present ();
425 #endif
426
427         {
428                 struct group *grp;
429                 /*
430                  * Start with a quick check to see if the group exists.
431                  */
432                 grp = getgrnam (group_name); /* local, no need for xgetgrnam */
433                 if (NULL == grp) {
434                         fprintf (stderr,
435                                  _("%s: group '%s' does not exist\n"),
436                                  Prog, group_name);
437                         exit (E_NOTFOUND);
438                 }
439
440                 group_id = grp->gr_gid;
441         }
442
443 #ifdef  USE_NIS
444         /*
445          * Make sure this isn't a NIS group
446          */
447         if (__isgrNIS ()) {
448                 char *nis_domain;
449                 char *nis_master;
450
451                 fprintf (stderr,
452                          _("%s: group '%s' is a NIS group\n"),
453                          Prog, group_name);
454
455                 if (!yp_get_default_domain (&nis_domain) &&
456                     !yp_master (nis_domain, "group.byname", &nis_master)) {
457                         fprintf (stderr,
458                                  _("%s: %s is the NIS master\n"),
459                                  Prog, nis_master);
460                 }
461                 exit (E_NOTFOUND);
462         }
463 #endif
464
465         /*
466          * Make sure this isn't the primary group of anyone.
467          */
468         group_busy (group_id);
469
470         /*
471          * Do the hard stuff - open the files, delete the group entries,
472          * then close and update the files.
473          */
474         open_files ();
475
476         grp_update ();
477
478         close_files ();
479
480         nscd_flush_cache ("group");
481
482         return E_SUCCESS;
483 }
484