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.31 2005/04/06 04:26:06 kloczek Exp $")
34 #include <sys/types.h>
40 #include "prototypes.h"
48 #include <security/pam_appl.h>
49 #include <security/pam_misc.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);
167 * Write out the new shadow group entries as well.
169 if (is_shadow_grp && !sgr_update (&sgrp)) {
170 fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
171 fail_exit (E_GRP_UPDATE);
173 #endif /* SHADOWGRP */
174 SYSLOG ((LOG_INFO, "new group: name=%s, gid=%u",
175 group_name, (unsigned int) group_id));
179 * find_new_gid - find the next available GID
181 * find_new_gid() locates the next highest unused GID in the group
182 * file, or checks the given group ID against the existing ones for
186 static void find_new_gid (void)
188 const struct group *grp;
189 gid_t gid_min, gid_max;
191 gid_min = getdef_unum ("GID_MIN", 100);
192 gid_max = getdef_unum ("GID_MAX", 60000);
195 * Start with some GID value if the user didn't provide us with
203 * Search the entire group file, either looking for this GID (if the
204 * user specified one with -g) or looking for the largest unused
210 while ((grp = gr_next ())) {
213 while ((grp = getgrent ())) {
215 if (strcmp (group_name, grp->gr_name) == 0) {
217 fail_exit (E_SUCCESS);
219 fprintf (stderr, _("%s: name %s is not unique\n"),
221 fail_exit (E_NAME_IN_USE);
223 if (gflg && group_id == grp->gr_gid) {
225 /* turn off -g and search again */
234 fprintf (stderr, _("%s: gid %u is not unique\n"),
235 Prog, (unsigned int) group_id);
236 fail_exit (E_GID_IN_USE);
238 if (!gflg && grp->gr_gid >= group_id) {
239 if (grp->gr_gid > gid_max)
241 group_id = grp->gr_gid + 1;
244 if (!gflg && group_id == gid_max + 1) {
245 for (group_id = gid_min; group_id < gid_max; group_id++) {
248 while ((grp = gr_next ())
249 && grp->gr_gid != group_id);
253 if (!getgrgid (group_id))
257 if (group_id == gid_max) {
258 fprintf (stderr, _("%s: can't get unique gid\n"), Prog);
259 fail_exit (E_GID_IN_USE);
265 * check_new_name - check the new name for validity
267 * check_new_name() insures that the new name doesn't contain any
268 * illegal characters.
271 static void check_new_name (void)
273 if (check_group_name (group_name))
277 * All invalid group names land here.
280 fprintf (stderr, _("%s: %s is not a valid group name\n"),
287 * process_flags - perform command line argument setting
289 * process_flags() interprets the command line arguments and sets the
290 * values that the user will be created with accordingly. The values
291 * are checked for sanity.
294 static void process_flags (int argc, char **argv)
299 while ((arg = getopt (argc, argv, "og:O:f")) != EOF) {
303 if (!isdigit (optarg[0]))
306 group_id = strtoul (optarg, &cp, 10);
309 _("%s: invalid group %s\n"), Prog,
311 fail_exit (E_BAD_ARG);
319 * override login.defs defaults (-O name=value)
320 * example: -O GID_MIN=100 -O GID_MAX=499
321 * note: -O GID_MIN=10,GID_MAX=499 doesn't work yet
323 cp = strchr (optarg, '=');
326 _("%s: -O requires NAME=VALUE\n"),
330 /* terminate name, point to value */
332 if (putdef_str (optarg, cp) < 0)
337 * "force" - do nothing, just exit(0), if the
338 * specified group already exists. With -g, if
339 * specified gid already exists, choose another
340 * (unique) gid (turn off -g). Based on the RedHat's
341 * patch from shadow-utils-970616-9.
353 if (optind != argc - 1)
356 group_name = argv[argc - 1];
361 * close_files - close all of the files that were opened
363 * close_files() closes all of the files that were opened for this new
364 * group. This causes any modified entries to be written out.
367 static void close_files (void)
370 fprintf (stderr, _("%s: cannot rewrite group file\n"), Prog);
371 fail_exit (E_GRP_UPDATE);
375 if (is_shadow_grp && !sgr_close ()) {
377 _("%s: cannot rewrite shadow group file\n"), Prog);
378 fail_exit (E_GRP_UPDATE);
382 #endif /* SHADOWGRP */
386 * open_files - lock and open the group files
388 * open_files() opens the two group files.
391 static void open_files (void)
394 fprintf (stderr, _("%s: unable to lock group file\n"), Prog);
397 if (!gr_open (O_RDWR)) {
398 fprintf (stderr, _("%s: unable to open group file\n"), Prog);
399 fail_exit (E_GRP_UPDATE);
402 if (is_shadow_grp && !sgr_lock ()) {
404 _("%s: unable to lock shadow group file\n"), Prog);
405 fail_exit (E_GRP_UPDATE);
407 if (is_shadow_grp && !sgr_open (O_RDWR)) {
409 _("%s: unable to open shadow group file\n"), Prog);
410 fail_exit (E_GRP_UPDATE);
412 #endif /* SHADOWGRP */
416 * fail_exit - exit with an error code after unlocking files
419 static void fail_exit (int code)
430 static struct pam_conv conv = {
437 * main - groupadd command
440 int main (int argc, char **argv)
443 pam_handle_t *pamh = NULL;
444 struct passwd *pampw;
449 * Get my name so that I can use it to report errors.
452 Prog = Basename (argv[0]);
454 setlocale (LC_ALL, "");
455 bindtextdomain (PACKAGE, LOCALEDIR);
456 textdomain (PACKAGE);
458 OPENLOG ("groupadd");
460 process_flags (argc, argv);
463 retval = PAM_SUCCESS;
465 pampw = getpwuid (getuid ());
467 retval = PAM_USER_UNKNOWN;
470 if (retval == PAM_SUCCESS) {
471 retval = pam_start ("groupadd", pampw->pw_name, &conv, &pamh);
474 if (retval == PAM_SUCCESS) {
475 retval = pam_authenticate (pamh, 0);
476 if (retval != PAM_SUCCESS) {
477 pam_end (pamh, retval);
481 if (retval == PAM_SUCCESS) {
482 retval = pam_acct_mgmt (pamh, 0);
483 if (retval != PAM_SUCCESS) {
484 pam_end (pamh, retval);
488 if (retval != PAM_SUCCESS) {
489 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
493 OPENLOG ("groupadd");
497 is_shadow_grp = sgr_file_present ();
501 * Start with a quick check to see if the group exists.
504 if (getgrnam (group_name)) {
508 fprintf (stderr, _("%s: group %s exists\n"), Prog, group_name);
509 exit (E_NAME_IN_USE);
513 * Do the hard stuff - open the files, create the group entries,
514 * then close and update the files.
522 nscd_flush_cache ("group");
527 if (retval == PAM_SUCCESS) {
528 retval = pam_chauthtok (pamh, 0);
529 if (retval != PAM_SUCCESS) {
530 pam_end (pamh, retval);
534 if (retval != PAM_SUCCESS) {
535 fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog);
539 if (retval == PAM_SUCCESS)
540 pam_end (pamh, PAM_SUCCESS);