Initial commit for Tizen
[profile/extras/shadow-utils.git] / src / groupadd.c
1 /*
2  * Copyright (c) 1991 - 1993, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2000 - 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: groupadd.c 3015 2009-06-05 22:16:56Z nekral-guest $"
36
37 #include <ctype.h>
38 #include <fcntl.h>
39 #include <getopt.h>
40 #include <grp.h>
41 #include <stdio.h>
42 #include <sys/types.h>
43 #ifdef ACCT_TOOLS_SETUID
44 #ifdef USE_PAM
45 #include "pam_defs.h"
46 #include <pwd.h>
47 #endif                          /* USE_PAM */
48 #endif                          /* ACCT_TOOLS_SETUID */
49 #include "chkname.h"
50 #include "defines.h"
51 #include "getdef.h"
52 #include "groupio.h"
53 #include "nscd.h"
54 #include "prototypes.h"
55 #ifdef  SHADOWGRP
56 #include "sgroupio.h"
57 #endif
58
59 /*
60  * exit status values
61  */
62 /*@-exitarg@*/
63 #define E_SUCCESS       0       /* success */
64 #define E_USAGE         2       /* invalid command syntax */
65 #define E_BAD_ARG       3       /* invalid argument to option */
66 #define E_GID_IN_USE    4       /* gid not unique (when -o not used) */
67 #define E_NAME_IN_USE   9       /* group name not unique */
68 #define E_GRP_UPDATE    10      /* can't update group file */
69
70 /*
71  * Global variables
72  */
73 char *Prog;
74
75 static /*@null@*/char *group_name;
76 static gid_t group_id;
77 static /*@null@*/char *group_passwd;
78 static /*@null@*/char *empty_list = NULL;
79
80 static bool oflg = false;       /* permit non-unique group ID to be specified with -g */
81 static bool gflg = false;       /* ID value for the new group */
82 static bool fflg = false;       /* if group already exists, do nothing and exit(0) */
83 static bool rflg = false;       /* create a system account */
84 static bool pflg = false;       /* new encrypted password */
85
86 #ifdef SHADOWGRP
87 static bool is_shadow_grp;
88 #endif
89
90 /* local function prototypes */
91 static void usage (void);
92 static void new_grent (struct group *grent);
93
94 #ifdef SHADOWGRP
95 static void new_sgent (struct sgrp *sgent);
96 #endif
97 static void grp_update (void);
98 static void check_new_name (void);
99 static void close_files (void);
100 static void open_files (void);
101 static void process_flags (int argc, char **argv);
102 static void check_flags (void);
103 static void check_perms (void);
104
105 /*
106  * usage - display usage message and exit
107  */
108 static void usage (void)
109 {
110         (void) fprintf (stderr,
111                         _("Usage: %s [options] GROUP\n"
112                           "\n"
113                           "Options:\n"),
114                         Prog);
115         (void) fputs (_("  -f, --force                   exit successfully if the group already exists,\n"
116                         "                                and cancel -g if the GID is already used\n"), stderr);
117         (void) fputs (_("  -g, --gid GID                 use GID for the new group\n"), stderr);
118         (void) fputs (_("  -h, --help                    display this help message and exit\n"), stderr);
119         (void) fputs (_("  -K, --key KEY=VALUE           override /etc/login.defs defaults\n"), stderr);
120         (void) fputs (_("  -o, --non-unique              allow to create groups with duplicate\n"
121                         "                                (non-unique) GID\n"), stderr);
122         (void) fputs (_("  -p, --password PASSWORD       use this encrypted password for the new group\n"), stderr);
123         (void) fputs (_("  -r, --system                  create a system account\n"), stderr);
124         (void) fputs ("\n", stderr);
125         exit (E_USAGE);
126 }
127
128 /*
129  * new_grent - initialize the values in a group file entry
130  *
131  *      new_grent() takes all of the values that have been entered and fills
132  *      in a (struct group) with them.
133  */
134 static void new_grent (struct group *grent)
135 {
136         memzero (grent, sizeof *grent);
137         grent->gr_name = group_name;
138         if (pflg) {
139                 grent->gr_passwd = group_passwd;
140         } else {
141                 grent->gr_passwd = SHADOW_PASSWD_STRING;        /* XXX warning: const */
142         }
143         grent->gr_gid = group_id;
144         grent->gr_mem = &empty_list;
145 }
146
147 #ifdef  SHADOWGRP
148 /*
149  * new_sgent - initialize the values in a shadow group file entry
150  *
151  *      new_sgent() takes all of the values that have been entered and fills
152  *      in a (struct sgrp) with them.
153  */
154 static void new_sgent (struct sgrp *sgent)
155 {
156         memzero (sgent, sizeof *sgent);
157         sgent->sg_name = group_name;
158         if (pflg) {
159                 sgent->sg_passwd = group_passwd;
160         } else {
161                 sgent->sg_passwd = "!"; /* XXX warning: const */
162         }
163         sgent->sg_adm = &empty_list;
164         sgent->sg_mem = &empty_list;
165 }
166 #endif                          /* SHADOWGRP */
167
168 /*
169  * grp_update - add new group file entries
170  *
171  *      grp_update() writes the new records to the group files.
172  */
173 static void grp_update (void)
174 {
175         struct group grp;
176
177 #ifdef  SHADOWGRP
178         struct sgrp sgrp;
179 #endif                          /* SHADOWGRP */
180
181         /*
182          * To add the group, we need to update /etc/group.
183          * Make sure failures will be reported.
184          */
185         add_cleanup (cleanup_report_add_group_group, group_name);
186 #ifdef  SHADOWGRP
187         if (is_shadow_grp) {
188                 /* We also need to update /etc/gshadow */
189                 add_cleanup (cleanup_report_add_group_gshadow, group_name);
190         }
191 #endif
192
193         /*
194          * Create the initial entries for this new group.
195          */
196         new_grent (&grp);
197 #ifdef  SHADOWGRP
198         new_sgent (&sgrp);
199         if (is_shadow_grp && pflg) {
200                 grp.gr_passwd = SHADOW_PASSWD_STRING;   /* XXX warning: const */
201         }
202 #endif                          /* SHADOWGRP */
203
204         /*
205          * Write out the new group file entry.
206          */
207         if (gr_update (&grp) == 0) {
208                 fprintf (stderr,
209                          _("%s: failed to prepare the new %s entry '%s'\n"),
210                          Prog, gr_dbname (), grp.gr_name);
211                 exit (E_GRP_UPDATE);
212         }
213 #ifdef  SHADOWGRP
214         /*
215          * Write out the new shadow group entries as well.
216          */
217         if (is_shadow_grp && (sgr_update (&sgrp) == 0)) {
218                 fprintf (stderr,
219                          _("%s: failed to prepare the new %s entry '%s'\n"),
220                          Prog, sgr_dbname (), sgrp.sg_name);
221                 exit (E_GRP_UPDATE);
222         }
223 #endif                          /* SHADOWGRP */
224 }
225
226 /*
227  * check_new_name - check the new name for validity
228  *
229  *      check_new_name() insures that the new name doesn't contain any
230  *      illegal characters.
231  */
232 static void check_new_name (void)
233 {
234         if (is_valid_group_name (group_name)) {
235                 return;
236         }
237
238         /*
239          * All invalid group names land here.
240          */
241
242         fprintf (stderr, _("%s: '%s' is not a valid group name\n"),
243                  Prog, group_name);
244
245         exit (E_BAD_ARG);
246 }
247
248 /*
249  * close_files - close all of the files that were opened
250  *
251  *      close_files() closes all of the files that were opened for this new
252  *      group. This causes any modified entries to be written out.
253  */
254 static void close_files (void)
255 {
256         /* First, write the changes in the regular group database */
257         if (gr_close () == 0) {
258                 fprintf (stderr,
259                          _("%s: failure while writing changes to %s\n"),
260                          Prog, gr_dbname ());
261                 exit (E_GRP_UPDATE);
262         }
263 #ifdef WITH_AUDIT
264         audit_logger (AUDIT_ADD_GROUP, Prog,
265                       "adding group to /etc/group",
266                       group_name, (unsigned int) group_id,
267                       SHADOW_AUDIT_SUCCESS);
268 #endif
269         SYSLOG ((LOG_INFO, "group added to %s: name=%s, GID=%u",
270                  gr_dbname (), group_name, (unsigned int) group_id));
271         del_cleanup (cleanup_report_add_group_group);
272
273         cleanup_unlock_group (NULL);
274         del_cleanup (cleanup_unlock_group);
275
276         /* Now, write the changes in the shadow database */
277 #ifdef  SHADOWGRP
278         if (is_shadow_grp) {
279                 if (sgr_close () == 0) {
280                         fprintf (stderr,
281                                  _("%s: failure while writing changes to %s\n"),
282                                  Prog, sgr_dbname ());
283                         exit (E_GRP_UPDATE);
284                 }
285 #ifdef WITH_AUDIT
286                 audit_logger (AUDIT_ADD_GROUP, Prog,
287                               "adding group to /etc/gshadow",
288                               group_name, (unsigned int) group_id,
289                               SHADOW_AUDIT_SUCCESS);
290 #endif
291                 SYSLOG ((LOG_INFO, "group added to %s: name=%s",
292                          sgr_dbname (), group_name));
293                 del_cleanup (cleanup_report_add_group_gshadow);
294
295                 cleanup_unlock_gshadow (NULL);
296                 del_cleanup (cleanup_unlock_gshadow);
297         }
298 #endif                          /* SHADOWGRP */
299
300         /* Report success at the system level */
301 #ifdef WITH_AUDIT
302         audit_logger (AUDIT_ADD_GROUP, Prog,
303                       "",
304                       group_name, (unsigned int) group_id,
305                       SHADOW_AUDIT_SUCCESS);
306 #endif
307         SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
308                  group_name, (unsigned int) group_id));
309         del_cleanup (cleanup_report_add_group);
310 }
311
312 /*
313  * open_files - lock and open the group files
314  *
315  *      open_files() opens the two group files.
316  */
317 static void open_files (void)
318 {
319         /* First, lock the databases */
320         if (gr_lock () == 0) {
321                 fprintf (stderr,
322                          _("%s: cannot lock %s; try again later.\n"),
323                          Prog, gr_dbname ());
324                 exit (E_GRP_UPDATE);
325         }
326         add_cleanup (cleanup_unlock_group, NULL);
327
328 #ifdef  SHADOWGRP
329         if (is_shadow_grp) {
330                 if (sgr_lock () == 0) {
331                         fprintf (stderr,
332                                  _("%s: cannot lock %s; try again later.\n"),
333                                  Prog, sgr_dbname ());
334                         exit (E_GRP_UPDATE);
335                 }
336                 add_cleanup (cleanup_unlock_gshadow, NULL);
337         }
338 #endif                          /* SHADOWGRP */
339
340         /*
341          * Now if the group is not added, it's our fault.
342          * Make sure failures will be reported.
343          */
344         add_cleanup (cleanup_report_add_group, group_name);
345
346         /* An now open the databases */
347         if (gr_open (O_RDWR) == 0) {
348                 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
349                 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
350                 exit (E_GRP_UPDATE);
351         }
352
353 #ifdef  SHADOWGRP
354         if (is_shadow_grp) {
355                 if (sgr_open (O_RDWR) == 0) {
356                         fprintf (stderr,
357                                  _("%s: cannot open %s\n"),
358                                  Prog, sgr_dbname ());
359                         SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
360                         exit (E_GRP_UPDATE);
361                 }
362         }
363 #endif                          /* SHADOWGRP */
364 }
365
366 /*
367  * process_flags - parse the command line options
368  *
369  *      It will not return if an error is encountered.
370  */
371 static void process_flags (int argc, char **argv)
372 {
373         /*
374          * Parse the command line options.
375          */
376         char *cp;
377         int option_index = 0;
378         int c;
379         static struct option long_options[] = {
380                 {"force", no_argument, NULL, 'f'},
381                 {"gid", required_argument, NULL, 'g'},
382                 {"help", no_argument, NULL, 'h'},
383                 {"key", required_argument, NULL, 'K'},
384                 {"non-unique", no_argument, NULL, 'o'},
385                 {"password", required_argument, NULL, 'p'},
386                 {"system", no_argument, NULL, 'r'},
387                 {NULL, 0, NULL, '\0'}
388         };
389
390         while ((c =
391                 getopt_long (argc, argv, "fg:hK:op:r", long_options,
392                              &option_index)) != -1) {
393                 switch (c) {
394                 case 'f':
395                         /*
396                          * "force" - do nothing, just exit(0), if the
397                          * specified group already exists. With -g, if
398                          * specified gid already exists, choose another
399                          * (unique) gid (turn off -g). Based on the RedHat's
400                          * patch from shadow-utils-970616-9.
401                          */
402                         fflg = true;
403                         break;
404                 case 'g':
405                         gflg = true;
406                         if (   (get_gid (optarg, &group_id) == 0)
407                             || (group_id == (gid_t)-1)) {
408                                 fprintf (stderr,
409                                          _("%s: invalid group ID '%s'\n"),
410                                          Prog, optarg);
411                                 exit (E_BAD_ARG);
412                         }
413                         break;
414                 case 'h':
415                         usage ();
416                         break;
417                 case 'K':
418                         /*
419                          * override login.defs defaults (-K name=value)
420                          * example: -K GID_MIN=100 -K GID_MAX=499
421                          * note: -K GID_MIN=10,GID_MAX=499 doesn't work yet
422                          */
423                         cp = strchr (optarg, '=');
424                         if (NULL == cp) {
425                                 fprintf (stderr,
426                                          _("%s: -K requires KEY=VALUE\n"),
427                                          Prog);
428                                 exit (E_BAD_ARG);
429                         }
430                         /* terminate name, point to value */
431                         *cp++ = '\0';
432                         if (putdef_str (optarg, cp) < 0) {
433                                 exit (E_BAD_ARG);
434                         }
435                         break;
436                 case 'o':
437                         oflg = true;
438                         break;
439                 case 'p':
440                         pflg = true;
441                         group_passwd = optarg;
442                         break;
443                 case 'r':
444                         rflg = true;
445                         break;
446                 default:
447                         usage ();
448                 }
449         }
450
451         /*
452          * Check the flags consistency
453          */
454         if (optind != argc - 1) {
455                 usage ();
456         }
457         group_name = argv[optind];
458
459         check_flags ();
460 }
461
462 /*
463  * check_flags - check flags and parameters consistency
464  *
465  *      It will not return if an error is encountered.
466  */
467 static void check_flags (void)
468 {
469         /* -o does not make sense without -g */
470         if (oflg && !gflg) {
471                 usage ();
472         }
473
474         check_new_name ();
475
476         /*
477          * Check if the group already exist.
478          */
479         /* local, no need for xgetgrnam */
480         if (getgrnam (group_name) != NULL) {
481                 /* The group already exist */
482                 if (fflg) {
483                         /* OK, no need to do anything */
484                         exit (E_SUCCESS);
485                 }
486                 fprintf (stderr,
487                          _("%s: group '%s' already exists\n"),
488                          Prog, group_name);
489                 exit (E_NAME_IN_USE);
490         }
491
492         if (gflg && (getgrgid (group_id) != NULL)) {
493                 /* A GID was specified, and a group already exist with that GID
494                  *  - either we will use this GID anyway (-o)
495                  *  - either we ignore the specified GID and
496                  *    we will use another one (-f)
497                  *  - either it is a failure
498                  */
499                 if (oflg) {
500                         /* Continue with this GID */
501                 } else if (fflg) {
502                         /* Turn off -g, we can use any GID */
503                         gflg = false;
504                 } else {
505                         fprintf (stderr,
506                                  _("%s: GID '%lu' already exists\n"),
507                                  Prog, (unsigned long int) group_id);
508                         exit (E_GID_IN_USE);
509                 }
510         }
511 }
512
513 /*
514  * check_perms - check if the caller is allowed to add a group
515  *
516  *      With PAM support, the setuid bit can be set on groupadd to allow
517  *      non-root users to groups.
518  *      Without PAM support, only users who can write in the group databases
519  *      can add groups.
520  *
521  *      It will not return if the user is not allowed.
522  */
523 static void check_perms (void)
524 {
525 #ifdef ACCT_TOOLS_SETUID
526 #ifdef USE_PAM
527         pam_handle_t *pamh = NULL;
528         int retval;
529         struct passwd *pampw;
530
531         pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
532         if (NULL == pampw) {
533                 fprintf (stderr,
534                          _("%s: Cannot determine your user name.\n"),
535                          Prog);
536                 exit (1);
537         }
538
539         retval = pam_start ("groupadd", pampw->pw_name, &conv, &pamh);
540
541         if (PAM_SUCCESS == retval) {
542                 retval = pam_authenticate (pamh, 0);
543         }
544
545         if (PAM_SUCCESS == retval) {
546                 retval = pam_acct_mgmt (pamh, 0);
547         }
548
549         if (NULL != pamh) {
550                 (void) pam_end (pamh, retval);
551         }
552         if (PAM_SUCCESS != retval) {
553                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
554                 exit (1);
555         }
556 #endif                          /* USE_PAM */
557 #endif                          /* ACCT_TOOLS_SETUID */
558 }
559
560 /*
561  * main - groupadd command
562  */
563 int main (int argc, char **argv)
564 {
565 #ifdef WITH_AUDIT
566         audit_help_open ();
567 #endif
568         atexit (do_cleanups);
569
570         /*
571          * Get my name so that I can use it to report errors.
572          */
573         Prog = Basename (argv[0]);
574
575         (void) setlocale (LC_ALL, "");
576         (void) bindtextdomain (PACKAGE, LOCALEDIR);
577         (void) textdomain (PACKAGE);
578
579         OPENLOG ("groupadd");
580
581         /*
582          * Parse the command line options.
583          */
584         process_flags (argc, argv);
585
586         check_perms ();
587
588 #ifdef SHADOWGRP
589         is_shadow_grp = sgr_file_present ();
590 #endif
591
592         /*
593          * Do the hard stuff - open the files, create the group entries,
594          * then close and update the files.
595          */
596         open_files ();
597
598         if (!gflg) {
599                 if (find_new_gid (rflg, &group_id, NULL) < 0) {
600                         exit (E_GID_IN_USE);
601                 }
602         }
603
604         grp_update ();
605         close_files ();
606
607         nscd_flush_cache ("group");
608
609         exit (E_SUCCESS);
610         /*@notreached@*/
611 }
612