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