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