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);
94 static void check_perms (void);
97 * usage - display usage message and exit
99 static void usage (void)
101 fprintf (stderr, _("Usage: groupadd [options] GROUP\n"
104 " -f, --force force exit with success status if the\n"
105 " specified group already exists\n"
106 " -g, --gid GID use GID for the new group\n"
107 " -h, --help display this help message and exit\n"
108 " -K, --key KEY=VALUE overrides /etc/login.defs defaults\n"
109 " -o, --non-unique allow create group with duplicate\n"
110 " (non-unique) GID\n"
116 * new_grent - initialize the values in a group file entry
118 * new_grent() takes all of the values that have been entered and fills
119 * in a (struct group) with them.
121 static void new_grent (struct group *grent)
123 memzero (grent, sizeof *grent);
124 grent->gr_name = group_name;
125 grent->gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
126 grent->gr_gid = group_id;
127 grent->gr_mem = &empty_list;
132 * new_sgent - initialize the values in a shadow group file entry
134 * new_sgent() takes all of the values that have been entered and fills
135 * in a (struct sgrp) with them.
137 static void new_sgent (struct sgrp *sgent)
139 memzero (sgent, sizeof *sgent);
140 sgent->sg_name = group_name;
141 sgent->sg_passwd = "!"; /* XXX warning: const */
142 sgent->sg_adm = &empty_list;
143 sgent->sg_mem = &empty_list;
145 #endif /* SHADOWGRP */
148 * grp_update - add new group file entries
150 * grp_update() writes the new records to the group files.
152 static void grp_update (void)
158 #endif /* SHADOWGRP */
161 * Create the initial entries for this new group.
166 #endif /* SHADOWGRP */
169 * Write out the new group file entry.
171 if (!gr_update (&grp)) {
172 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
173 fail_exit (E_GRP_UPDATE);
177 * Write out the new shadow group entries as well.
179 if (is_shadow_grp && !sgr_update (&sgrp)) {
180 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
181 fail_exit (E_GRP_UPDATE);
183 #endif /* SHADOWGRP */
185 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group", group_name,
188 SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
189 group_name, (unsigned int) group_id));
193 * find_new_gid - find the next available GID
195 * find_new_gid() locates the next highest unused GID in the group
198 static void find_new_gid (void)
200 const struct group *grp;
201 gid_t gid_min, gid_max;
204 * It doesn't make sense to use find_new_uid(),
205 * if a GID is specified via "-g" option.
209 gid_min = getdef_unum ("GID_MIN", 1000);
210 gid_max = getdef_unum ("GID_MAX", 60000);
213 * Start with the lowest GID.
218 * Search the entire group file, looking for the largest unused
222 while ((grp = getgrent ())) {
223 if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) {
224 group_id = grp->gr_gid + 1;
227 if (group_id == (gid_max + 1)) {
228 for (group_id = gid_min; group_id < gid_max; group_id++) {
229 /* local, no need for xgetgrgid */
230 if (!getgrgid (group_id)) {
234 if (group_id == gid_max) {
235 fprintf (stderr, _("%s: can't get unique GID\n"), Prog);
236 fail_exit (E_GID_IN_USE);
242 * check_new_name - check the new name for validity
244 * check_new_name() insures that the new name doesn't contain any
245 * illegal characters.
247 static void check_new_name (void)
249 if (check_group_name (group_name)) {
254 * All invalid group names land here.
257 fprintf (stderr, _("%s: %s is not a valid group name\n"),
264 * close_files - close all of the files that were opened
266 * close_files() closes all of the files that were opened for this new
267 * group. This causes any modified entries to be written out.
269 static void close_files (void)
272 fprintf (stderr, _("%s: cannot rewrite group file\n"), Prog);
273 fail_exit (E_GRP_UPDATE);
277 if (is_shadow_grp && !sgr_close ()) {
279 _("%s: cannot rewrite shadow group file\n"), Prog);
280 fail_exit (E_GRP_UPDATE);
285 #endif /* SHADOWGRP */
289 * open_files - lock and open the group files
291 * open_files() opens the two group files.
293 static void open_files (void)
296 fprintf (stderr, _("%s: unable to lock group file\n"), Prog);
298 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "locking group file",
303 if (!gr_open (O_RDWR)) {
304 fprintf (stderr, _("%s: unable to open group file\n"), Prog);
306 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "opening group file",
309 fail_exit (E_GRP_UPDATE);
312 if (is_shadow_grp && !sgr_lock ()) {
314 _("%s: unable to lock shadow group file\n"), Prog);
315 fail_exit (E_GRP_UPDATE);
317 if (is_shadow_grp && !sgr_open (O_RDWR)) {
319 _("%s: unable to open shadow group file\n"), Prog);
320 fail_exit (E_GRP_UPDATE);
322 #endif /* SHADOWGRP */
326 * fail_exit - exit with an error code after unlocking files
328 static void fail_exit (int code)
338 if (code != E_SUCCESS) {
339 audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group",
347 * get_id - validate and get group ID
349 static gid_t get_gid (const char *gidstr)
354 val = strtol (gidstr, &errptr, 10);
355 if (('\0' != *errptr) || (errno == ERANGE) || (val < 0)) {
356 fprintf (stderr, _("%s: invalid numeric argument '%s'\n"),
364 * process_flags - parse the command line options
366 * It will not return if an error is encountered.
368 static void process_flags (int argc, char **argv)
371 * Parse the command line options.
374 int option_index = 0;
376 static struct option long_options[] = {
377 {"force", no_argument, NULL, 'f'},
378 {"gid", required_argument, NULL, 'g'},
379 {"help", no_argument, NULL, 'h'},
380 {"key", required_argument, NULL, 'K'},
381 {"non-unique", required_argument, NULL, 'o'},
382 {NULL, 0, NULL, '\0'}
386 getopt_long (argc, argv, "fg:hK:o", long_options,
387 &option_index)) != -1) {
391 * "force" - do nothing, just exit(0), if the
392 * specified group already exists. With -g, if
393 * specified gid already exists, choose another
394 * (unique) gid (turn off -g). Based on the RedHat's
395 * patch from shadow-utils-970616-9.
401 group_id = get_gid (optarg);
408 * override login.defs defaults (-K name=value)
409 * example: -K GID_MIN=100 -K GID_MAX=499
410 * note: -K GID_MIN=10,GID_MAX=499 doesn't work yet
412 cp = strchr (optarg, '=');
416 ("%s: -K requires KEY=VALUE\n"),
420 /* terminate name, point to value */
422 if (putdef_str (optarg, cp) < 0) {
435 * Check the flags consistency
441 if (optind != argc - 1) {
445 group_name = argv[optind];
450 * Check if the group already exist.
452 /* local, no need for xgetgrnam */
453 if (getgrnam (group_name) != NULL) {
454 /* The group already exist */
456 /* OK, no need to do anything */
457 fail_exit (E_SUCCESS);
459 fprintf (stderr, _("%s: group %s exists\n"), Prog, group_name);
460 fail_exit (E_NAME_IN_USE);
463 if (gflg && (getgrgid (group_id) != NULL)) {
464 /* A GID was specified, and a group already exist with that GID
465 * - either we will use this GID anyway (-o)
466 * - either we ignore the specified GID and
467 * we will use another one(-f)
468 * - either it is a failure
471 /* Continue with this GID */
473 /* Turn off -g, we can use any GID */
476 fprintf (stderr, _("%s: GID %u is not unique\n"),
477 Prog, (unsigned int) group_id);
478 fail_exit (E_GID_IN_USE);
484 * check_perms - check if the caller is allowed to add a group
486 * With PAM support, the setuid bit can be set on groupadd to allow
487 * non-root users to groups.
488 * Without PAM support, only users who can write in the group databases
491 static void check_perms (void)
494 pam_handle_t *pamh = NULL;
495 int retval = PAM_SUCCESS;
496 struct passwd *pampw;
498 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
500 retval = PAM_USER_UNKNOWN;
503 if (retval == PAM_SUCCESS) {
504 retval = pam_start ("groupadd", pampw->pw_name,
508 if (retval == PAM_SUCCESS) {
509 retval = pam_authenticate (pamh, 0);
510 if (retval != PAM_SUCCESS) {
511 pam_end (pamh, retval);
515 if (retval == PAM_SUCCESS) {
516 retval = pam_acct_mgmt (pamh, 0);
517 if (retval != PAM_SUCCESS) {
518 pam_end (pamh, retval);
522 if (retval != PAM_SUCCESS) {
523 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
530 * main - groupadd command
532 int main (int argc, char **argv)
538 * Get my name so that I can use it to report errors.
540 Prog = Basename (argv[0]);
542 setlocale (LC_ALL, "");
543 bindtextdomain (PACKAGE, LOCALEDIR);
544 textdomain (PACKAGE);
546 OPENLOG ("groupadd");
549 * Parse the command line options.
551 process_flags (argc, argv);
556 is_shadow_grp = sgr_file_present ();
560 * Do the hard stuff - open the files, create the group entries,
561 * then close and update the files.
572 nscd_flush_cache ("group");
575 pam_end (pamh, PAM_SUCCESS);