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
33 RCSID (PKG_VER "$Id: groupadd.c,v 1.37 2005/07/18 13:17:45 kloczek Exp $")
34 #include <sys/types.h>
40 #include <security/pam_appl.h>
41 #include <security/pam_misc.h>
45 #include "prototypes.h"
52 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 */
66 static char *group_name;
67 static gid_t group_id;
68 static char *empty_list = NULL;
72 static int oflg = 0; /* permit non-unique group ID to be specified with -g */
73 static int gflg = 0; /* ID value for the new group */
74 static int fflg = 0; /* if group already exists, do nothing and exit(0) */
76 /* local function prototypes */
77 static void usage (void);
78 static void new_grent (struct group *);
81 static void new_sgent (struct sgrp *);
83 static void grp_update (void);
84 static void find_new_gid (void);
85 static void check_new_name (void);
86 static void process_flags (int, char **);
87 static void close_files (void);
88 static void open_files (void);
89 static void fail_exit (int);
92 * usage - display usage message and exit
95 static void usage (void)
97 fprintf (stderr, _("Usage: groupadd [-g gid [-o]] [-f] group\n"));
102 * new_grent - initialize the values in a group file entry
104 * new_grent() takes all of the values that have been entered and fills
105 * in a (struct group) with them.
108 static void new_grent (struct group *grent)
110 memzero (grent, sizeof *grent);
111 grent->gr_name = group_name;
112 grent->gr_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
113 grent->gr_gid = group_id;
114 grent->gr_mem = &empty_list;
119 * new_sgent - initialize the values in a shadow group file entry
121 * new_sgent() takes all of the values that have been entered and fills
122 * in a (struct sgrp) with them.
125 static void new_sgent (struct sgrp *sgent)
127 memzero (sgent, sizeof *sgent);
128 sgent->sg_name = group_name;
129 sgent->sg_passwd = "!"; /* XXX warning: const */
130 sgent->sg_adm = &empty_list;
131 sgent->sg_mem = &empty_list;
133 #endif /* SHADOWGRP */
136 * grp_update - add new group file entries
138 * grp_update() writes the new records to the group files.
141 static void grp_update (void)
147 #endif /* SHADOWGRP */
150 * Create the initial entries for this new group.
155 #endif /* SHADOWGRP */
158 * Write out the new group file entry.
160 if (!gr_update (&grp)) {
161 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
162 fail_exit (E_GRP_UPDATE);
166 * Write out the new shadow group entries as well.
168 if (is_shadow_grp && !sgr_update (&sgrp)) {
169 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
170 fail_exit (E_GRP_UPDATE);
172 #endif /* SHADOWGRP */
173 SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
174 group_name, (unsigned int) group_id));
178 * find_new_gid - find the next available GID
180 * find_new_gid() locates the next highest unused GID in the group
181 * file, or checks the given group ID against the existing ones for
185 static void find_new_gid (void)
187 const struct group *grp;
188 gid_t gid_min, gid_max;
190 gid_min = getdef_unum ("GID_MIN", 100);
191 gid_max = getdef_unum ("GID_MAX", 60000);
194 * Start with some GID value if the user didn't provide us with
202 * Search the entire group file, either looking for this GID (if the
203 * user specified one with -g) or looking for the largest unused
209 while ((grp = gr_next ())) {
212 while ((grp = getgrent ())) {
214 if (strcmp (group_name, grp->gr_name) == 0) {
216 fail_exit (E_SUCCESS);
218 fprintf (stderr, _("%s: name %s is not unique\n"),
220 fail_exit (E_NAME_IN_USE);
222 if (gflg && group_id == grp->gr_gid) {
224 /* turn off -g and search again */
233 fprintf (stderr, _("%s: GID %u is not unique\n"),
234 Prog, (unsigned int) group_id);
235 fail_exit (E_GID_IN_USE);
237 if (!gflg && grp->gr_gid >= group_id) {
238 if (grp->gr_gid > gid_max)
240 group_id = grp->gr_gid + 1;
243 if (!gflg && group_id == gid_max + 1) {
244 for (group_id = gid_min; group_id < gid_max; group_id++) {
247 while ((grp = gr_next ())
248 && grp->gr_gid != group_id);
252 if (!getgrgid (group_id))
256 if (group_id == gid_max) {
257 fprintf (stderr, _("%s: can't get unique GID\n"), Prog);
258 fail_exit (E_GID_IN_USE);
264 * check_new_name - check the new name for validity
266 * check_new_name() insures that the new name doesn't contain any
267 * illegal characters.
270 static void check_new_name (void)
272 if (check_group_name (group_name))
276 * All invalid group names land here.
279 fprintf (stderr, _("%s: %s is not a valid group name\n"),
286 * process_flags - perform command line argument setting
288 * process_flags() interprets the command line arguments and sets the
289 * values that the user will be created with accordingly. The values
290 * are checked for sanity.
293 static void process_flags (int argc, char **argv)
298 while ((arg = getopt (argc, argv, "fg:K:o")) != EOF) {
302 * "force" - do nothing, just exit(0), if the
303 * specified group already exists. With -g, if
304 * specified gid already exists, choose another
305 * (unique) gid (turn off -g). Based on the RedHat's
306 * patch from shadow-utils-970616-9.
312 if (!isdigit (optarg[0]))
315 group_id = strtoul (optarg, &cp, 10);
318 _("%s: invalid group %s\n"), Prog,
320 fail_exit (E_BAD_ARG);
325 * override login.defs defaults (-K name=value)
326 * example: -K GID_MIN=100 -K GID_MAX=499
327 * note: -K GID_MIN=10,GID_MAX=499 doesn't work yet
329 cp = strchr (optarg, '=');
332 _("%s: -K requires KEY=VALUE\n"),
336 /* terminate name, point to value */
338 if (putdef_str (optarg, cp) < 0)
352 if (optind != argc - 1)
355 group_name = argv[argc - 1];
360 * close_files - close all of the files that were opened
362 * close_files() closes all of the files that were opened for this new
363 * group. This causes any modified entries to be written out.
366 static void close_files (void)
369 fprintf (stderr, _("%s: cannot rewrite group file\n"), Prog);
370 fail_exit (E_GRP_UPDATE);
374 if (is_shadow_grp && !sgr_close ()) {
376 _("%s: cannot rewrite shadow group file\n"), Prog);
377 fail_exit (E_GRP_UPDATE);
381 #endif /* SHADOWGRP */
385 * open_files - lock and open the group files
387 * open_files() opens the two group files.
390 static void open_files (void)
393 fprintf (stderr, _("%s: unable to lock group file\n"), Prog);
396 if (!gr_open (O_RDWR)) {
397 fprintf (stderr, _("%s: unable to open group file\n"), Prog);
398 fail_exit (E_GRP_UPDATE);
401 if (is_shadow_grp && !sgr_lock ()) {
403 _("%s: unable to lock shadow group file\n"), Prog);
404 fail_exit (E_GRP_UPDATE);
406 if (is_shadow_grp && !sgr_open (O_RDWR)) {
408 _("%s: unable to open shadow group file\n"), Prog);
409 fail_exit (E_GRP_UPDATE);
411 #endif /* SHADOWGRP */
415 * fail_exit - exit with an error code after unlocking files
418 static void fail_exit (int code)
429 static struct pam_conv conv = {
436 * main - groupadd command
439 int main (int argc, char **argv)
442 pam_handle_t *pamh = NULL;
443 struct passwd *pampw;
448 * Get my name so that I can use it to report errors.
451 Prog = Basename (argv[0]);
453 setlocale (LC_ALL, "");
454 bindtextdomain (PACKAGE, LOCALEDIR);
455 textdomain (PACKAGE);
457 OPENLOG ("groupadd");
459 process_flags (argc, argv);
462 retval = PAM_SUCCESS;
464 pampw = getpwuid (getuid ());
466 retval = PAM_USER_UNKNOWN;
469 if (retval == PAM_SUCCESS) {
470 retval = pam_start ("groupadd", pampw->pw_name, &conv, &pamh);
473 if (retval == PAM_SUCCESS) {
474 retval = pam_authenticate (pamh, 0);
475 if (retval != PAM_SUCCESS) {
476 pam_end (pamh, retval);
480 if (retval == PAM_SUCCESS) {
481 retval = pam_acct_mgmt (pamh, 0);
482 if (retval != PAM_SUCCESS) {
483 pam_end (pamh, retval);
487 if (retval != PAM_SUCCESS) {
488 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
492 OPENLOG ("groupadd");
496 is_shadow_grp = sgr_file_present ();
500 * Start with a quick check to see if the group exists.
503 if (getgrnam (group_name)) {
507 fprintf (stderr, _("%s: group %s exists\n"), Prog, group_name);
508 exit (E_NAME_IN_USE);
512 * Do the hard stuff - open the files, create the group entries,
513 * then close and update the files.
521 nscd_flush_cache ("group");
526 if (retval == PAM_SUCCESS) {
527 retval = pam_chauthtok (pamh, 0);
528 if (retval != PAM_SUCCESS) {
529 pam_end (pamh, retval);
533 if (retval != PAM_SUCCESS) {
534 fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog);
538 if (retval == PAM_SUCCESS)
539 pam_end (pamh, PAM_SUCCESS);