2 * Copyright 1991 - 1993, Julianne Frances Haugh
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 #include <sys/types.h>
50 #include "prototypes.h"
53 static int is_shadow_grp;
59 #define E_SUCCESS 0 /* success */
60 #define E_USAGE 2 /* invalid command syntax */
61 #define E_BAD_ARG 3 /* invalid argument to option */
62 #define E_GID_IN_USE 4 /* gid not unique (when -o not used) */
63 #define E_NAME_IN_USE 9 /* group name not unique */
64 #define E_GRP_UPDATE 10 /* can't update group file */
69 static char *group_name;
70 static gid_t group_id;
71 static char *empty_list = NULL;
75 static int oflg = 0; /* permit non-unique group ID to be specified with -g */
76 static int gflg = 0; /* ID value for the new group */
77 static int fflg = 0; /* if group already exists, do nothing and exit(0) */
79 /* local function prototypes */
80 static void usage (void);
81 static void new_grent (struct group *grent);
84 static void new_sgent (struct sgrp *sgent);
86 static void grp_update (void);
87 static void find_new_gid (void);
88 static void check_new_name (void);
89 static void close_files (void);
90 static void open_files (void);
91 static void fail_exit (int code);
92 static gid_t get_gid (const char *gidstr);
93 static void process_flags (int argc, char **argv);
96 * usage - display usage message and exit
98 static void usage (void)
100 fprintf (stderr, _("Usage: groupadd [options] GROUP\n"
103 " -f, --force force exit with success status if the\n"
104 " specified group already exists\n"
105 " -g, --gid GID use GID for the new group\n"
106 " -h, --help display this help message and exit\n"
107 " -K, --key KEY=VALUE overrides /etc/login.defs defaults\n"
108 " -o, --non-unique allow create group with duplicate\n"
109 " (non-unique) GID\n"
115 * new_grent - initialize the values in a group file entry
117 * new_grent() takes all of the values that have been entered and fills
118 * in a (struct group) with them.
120 static void new_grent (struct group *grent)
122 memzero (grent, sizeof *grent);
123 grent->gr_name = group_name;
124 grent->gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
125 grent->gr_gid = group_id;
126 grent->gr_mem = &empty_list;
131 * new_sgent - initialize the values in a shadow group file entry
133 * new_sgent() takes all of the values that have been entered and fills
134 * in a (struct sgrp) with them.
136 static void new_sgent (struct sgrp *sgent)
138 memzero (sgent, sizeof *sgent);
139 sgent->sg_name = group_name;
140 sgent->sg_passwd = "!"; /* XXX warning: const */
141 sgent->sg_adm = &empty_list;
142 sgent->sg_mem = &empty_list;
144 #endif /* SHADOWGRP */
147 * grp_update - add new group file entries
149 * grp_update() writes the new records to the group files.
151 static void grp_update (void)
157 #endif /* SHADOWGRP */
160 * Create the initial entries for this new group.
165 #endif /* SHADOWGRP */
168 * Write out the new group file entry.
170 if (!gr_update (&grp)) {
171 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
172 fail_exit (E_GRP_UPDATE);
176 * Write out the new shadow group entries as well.
178 if (is_shadow_grp && !sgr_update (&sgrp)) {
179 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
180 fail_exit (E_GRP_UPDATE);
182 #endif /* SHADOWGRP */
184 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group", group_name,
187 SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
188 group_name, (unsigned int) group_id));
192 * find_new_gid - find the next available GID
194 * find_new_gid() locates the next highest unused GID in the group
197 static void find_new_gid (void)
199 const struct group *grp;
200 gid_t gid_min, gid_max;
203 * It doesn't make sense to use find_new_uid(),
204 * if a GID is specified via "-g" option.
208 gid_min = getdef_unum ("GID_MIN", 1000);
209 gid_max = getdef_unum ("GID_MAX", 60000);
212 * Start with the lowest GID.
217 * Search the entire group file, looking for the largest unused
221 while ((grp = getgrent ())) {
222 if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) {
223 group_id = grp->gr_gid + 1;
226 if (group_id == (gid_max + 1)) {
227 for (group_id = gid_min; group_id < gid_max; group_id++) {
228 /* local, no need for xgetgrgid */
229 if (!getgrgid (group_id)) {
233 if (group_id == gid_max) {
234 fprintf (stderr, _("%s: can't get unique GID\n"), Prog);
235 fail_exit (E_GID_IN_USE);
241 * check_new_name - check the new name for validity
243 * check_new_name() insures that the new name doesn't contain any
244 * illegal characters.
246 static void check_new_name (void)
248 if (check_group_name (group_name)) {
253 * All invalid group names land here.
256 fprintf (stderr, _("%s: %s is not a valid group name\n"),
263 * close_files - close all of the files that were opened
265 * close_files() closes all of the files that were opened for this new
266 * group. This causes any modified entries to be written out.
268 static void close_files (void)
271 fprintf (stderr, _("%s: cannot rewrite group file\n"), Prog);
272 fail_exit (E_GRP_UPDATE);
276 if (is_shadow_grp && !sgr_close ()) {
278 _("%s: cannot rewrite shadow group file\n"), Prog);
279 fail_exit (E_GRP_UPDATE);
284 #endif /* SHADOWGRP */
288 * open_files - lock and open the group files
290 * open_files() opens the two group files.
292 static void open_files (void)
295 fprintf (stderr, _("%s: unable to lock group file\n"), Prog);
297 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "locking group file",
302 if (!gr_open (O_RDWR)) {
303 fprintf (stderr, _("%s: unable to open group file\n"), Prog);
305 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "opening group file",
308 fail_exit (E_GRP_UPDATE);
311 if (is_shadow_grp && !sgr_lock ()) {
313 _("%s: unable to lock shadow group file\n"), Prog);
314 fail_exit (E_GRP_UPDATE);
316 if (is_shadow_grp && !sgr_open (O_RDWR)) {
318 _("%s: unable to open shadow group file\n"), Prog);
319 fail_exit (E_GRP_UPDATE);
321 #endif /* SHADOWGRP */
325 * fail_exit - exit with an error code after unlocking files
327 static void fail_exit (int code)
337 if (code != E_SUCCESS) {
338 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group",
346 * get_id - validate and get group ID
348 static gid_t get_gid (const char *gidstr)
353 val = strtol (gidstr, &errptr, 10);
354 if (('\0' != *errptr) || (errno == ERANGE) || (val < 0)) {
355 fprintf (stderr, _("%s: invalid numeric argument '%s'\n"),
363 * process_flags - parse the command line options
365 * It will not return if an error is encountered.
367 static void process_flags (int argc, char **argv)
370 * Parse the command line options.
373 int option_index = 0;
375 static struct option long_options[] = {
376 {"force", no_argument, NULL, 'f'},
377 {"gid", required_argument, NULL, 'g'},
378 {"help", no_argument, NULL, 'h'},
379 {"key", required_argument, NULL, 'K'},
380 {"non-unique", required_argument, NULL, 'o'},
381 {NULL, 0, NULL, '\0'}
385 getopt_long (argc, argv, "fg:hK:o", long_options,
386 &option_index)) != -1) {
390 * "force" - do nothing, just exit(0), if the
391 * specified group already exists. With -g, if
392 * specified gid already exists, choose another
393 * (unique) gid (turn off -g). Based on the RedHat's
394 * patch from shadow-utils-970616-9.
400 group_id = get_gid (optarg);
407 * override login.defs defaults (-K name=value)
408 * example: -K GID_MIN=100 -K GID_MAX=499
409 * note: -K GID_MIN=10,GID_MAX=499 doesn't work yet
411 cp = strchr (optarg, '=');
415 ("%s: -K requires KEY=VALUE\n"),
419 /* terminate name, point to value */
421 if (putdef_str (optarg, cp) < 0) {
434 * Check the flags consistency
440 if (optind != argc - 1) {
444 group_name = argv[optind];
449 * Check if the group already exist.
451 /* local, no need for xgetgrnam */
452 if (getgrnam (group_name) != NULL) {
453 /* The group already exist */
455 /* OK, no need to do anything */
456 fail_exit (E_SUCCESS);
458 fprintf (stderr, _("%s: group %s exists\n"), Prog, group_name);
459 fail_exit (E_NAME_IN_USE);
462 if (gflg && (getgrgid (group_id) != NULL)) {
463 /* A GID was specified, and a group already exist with that GID
464 * - either we will use this GID anyway (-o)
465 * - either we ignore the specified GID and
466 * we will use another one(-f)
467 * - either it is a failure
470 /* Continue with this GID */
472 /* Turn off -g, we can use any GID */
475 fprintf (stderr, _("%s: GID %u is not unique\n"),
476 Prog, (unsigned int) group_id);
477 fail_exit (E_GID_IN_USE);
483 * main - groupadd command
485 int main (int argc, char **argv)
488 pam_handle_t *pamh = NULL;
496 * Get my name so that I can use it to report errors.
498 Prog = Basename (argv[0]);
500 setlocale (LC_ALL, "");
501 bindtextdomain (PACKAGE, LOCALEDIR);
502 textdomain (PACKAGE);
504 OPENLOG ("groupadd");
507 * Parse the command line options.
509 process_flags (argc, argv);
512 retval = PAM_SUCCESS;
515 struct passwd *pampw;
516 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
518 retval = PAM_USER_UNKNOWN;
521 if (retval == PAM_SUCCESS) {
522 retval = pam_start ("groupadd", pampw->pw_name,
527 if (retval == PAM_SUCCESS) {
528 retval = pam_authenticate (pamh, 0);
529 if (retval != PAM_SUCCESS) {
530 pam_end (pamh, retval);
534 if (retval == PAM_SUCCESS) {
535 retval = pam_acct_mgmt (pamh, 0);
536 if (retval != PAM_SUCCESS) {
537 pam_end (pamh, retval);
541 if (retval != PAM_SUCCESS) {
542 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
548 is_shadow_grp = sgr_file_present ();
552 * Do the hard stuff - open the files, create the group entries,
553 * then close and update the files.
564 nscd_flush_cache ("group");
567 if (retval == PAM_SUCCESS) {
568 pam_end (pamh, PAM_SUCCESS);