]> granicus.if.org Git - shadow/blob - src/groupadd.c
[svn-upgrade] Integrating new upstream version, shadow (19990709)
[shadow] / src / groupadd.c
1 /*
2  * Copyright 1991 - 1993, Julianne Frances Haugh
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  */
29
30 #include <config.h>
31
32 #include "rcsid.h"
33 RCSID(PKG_VER "$Id: groupadd.c,v 1.14 1999/06/07 16:40:45 marekm Exp $")
34
35 #include <sys/types.h>
36 #include <stdio.h>
37 #include <grp.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40
41 #include "defines.h"
42 #include "prototypes.h"
43 #include "chkname.h"
44
45 #include "getdef.h"
46
47 #include "groupio.h"
48
49 #ifdef  SHADOWGRP
50 #include "sgroupio.h"
51
52 static int is_shadow_grp;
53 #endif
54
55 /*
56  * exit status values
57  */
58
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 nut unique */
64 #define E_GRP_UPDATE    10      /* can't update group file */
65
66 static char     *group_name;
67 static gid_t    group_id;
68 static char *empty_list = NULL;
69
70 static char     *Prog;
71
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) */
75
76 #ifdef  NDBM
77 extern  int     gr_dbm_mode;
78 extern  int     sg_dbm_mode;
79 #endif
80
81 extern int optind;
82 extern char *optarg;
83
84 /* local function prototypes */
85 static void usage P_((void));
86 static void new_grent P_((struct group *));
87 #ifdef SHADOWGRP
88 static void new_sgent P_((struct sgrp *));
89 #endif
90 static void grp_update P_((void));
91 static void find_new_gid P_((void));
92 static void check_new_name P_((void));
93 static void process_flags P_((int, char **));
94 static void close_files P_((void));
95 static void open_files P_((void));
96 static void fail_exit P_((int));
97 int main P_((int, char **));
98
99 /*
100  * usage - display usage message and exit
101  */
102
103 static void
104 usage(void)
105 {
106         fprintf(stderr, _("usage: groupadd [-g gid [-o]] group\n"));
107         exit(E_USAGE);
108 }
109
110 /*
111  * new_grent - initialize the values in a group file entry
112  *
113  *      new_grent() takes all of the values that have been entered and
114  *      fills in a (struct group) with them.
115  */
116
117 static void
118 new_grent(struct group *grent)
119 {
120         memzero(grent, sizeof *grent);
121         grent->gr_name = group_name;
122         grent->gr_passwd = SHADOW_PASSWD_STRING;  /* XXX warning: const */
123         grent->gr_gid = group_id;
124         grent->gr_mem = &empty_list;
125 }
126
127 #ifdef  SHADOWGRP
128 /*
129  * new_sgent - initialize the values in a shadow group file entry
130  *
131  *      new_sgent() takes all of the values that have been entered and
132  *      fills in a (struct sgrp) with them.
133  */
134
135 static void
136 new_sgent(struct sgrp *sgent)
137 {
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;
143 }
144 #endif  /* SHADOWGRP */
145
146 /*
147  * grp_update - add new group file entries
148  *
149  *      grp_update() writes the new records to the group files.
150  */
151
152 static void
153 grp_update(void)
154 {
155         struct  group   grp;
156 #ifdef  SHADOWGRP
157         struct  sgrp    sgrp;
158 #endif  /* SHADOWGRP */
159
160         /*
161          * Create the initial entries for this new group.
162          */
163
164         new_grent (&grp);
165 #ifdef  SHADOWGRP
166         new_sgent (&sgrp);
167 #endif  /* SHADOWGRP */
168
169         /*
170          * Write out the new group file entry.
171          */
172
173         if (! gr_update (&grp)) {
174                 fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
175                 fail_exit(E_GRP_UPDATE);
176         }
177 #ifdef  NDBM
178
179         /*
180          * Update the DBM group file with the new entry as well.
181          */
182
183         if (gr_dbm_present() && ! gr_dbm_update (&grp)) {
184                 fprintf(stderr, _("%s: cannot add new dbm group entry\n"), Prog);
185                 fail_exit(E_GRP_UPDATE);
186         }
187         endgrent ();
188 #endif  /* NDBM */
189
190 #ifdef  SHADOWGRP
191
192         /*
193          * Write out the new shadow group entries as well.
194          */
195
196         if (is_shadow_grp && ! sgr_update (&sgrp)) {
197                 fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
198                 fail_exit(E_GRP_UPDATE);
199         }
200 #ifdef  NDBM
201
202         /*
203          * Update the DBM group file with the new entry as well.
204          */
205
206         if (is_shadow_grp && sg_dbm_present() && ! sg_dbm_update (&sgrp)) {
207                 fprintf(stderr, _("%s: cannot add new dbm group entry\n"), Prog);
208                 fail_exit(E_GRP_UPDATE);
209         }
210         endsgent ();
211 #endif  /* NDBM */
212 #endif  /* SHADOWGRP */
213         SYSLOG((LOG_INFO, "new group: name=%s, gid=%d\n",
214                 group_name, group_id));
215 }
216
217 /*
218  * find_new_gid - find the next available GID
219  *
220  *      find_new_gid() locates the next highest unused GID in the group
221  *      file, or checks the given group ID against the existing ones for
222  *      uniqueness.
223  */
224
225 static void
226 find_new_gid(void)
227 {
228         const struct group *grp;
229         gid_t gid_min, gid_max;
230
231         gid_min = getdef_num("GID_MIN", 100);
232         gid_max = getdef_num("GID_MAX", 60000);
233
234         /*
235          * Start with some GID value if the user didn't provide us with
236          * one already.
237          */
238
239         if (! gflg)
240                 group_id = gid_min;
241
242         /*
243          * Search the entire group file, either looking for this
244          * GID (if the user specified one with -g) or looking for the
245          * largest unused value.
246          */
247
248 #ifdef NO_GETGRENT
249         gr_rewind();
250         while ((grp = gr_next())) {
251 #else
252         setgrent();
253         while ((grp = getgrent())) {
254 #endif
255                 if (strcmp(group_name, grp->gr_name) == 0) {
256                         if (fflg) {
257                                 fail_exit(E_SUCCESS);
258                         }
259                         fprintf(stderr, _("%s: name %s is not unique\n"),
260                                 Prog, group_name);
261                         fail_exit(E_NAME_IN_USE);
262                 }
263                 if (gflg && group_id == grp->gr_gid) {
264                         if (fflg) {
265                                 /* turn off -g and search again */
266                                 gflg = 0;
267 #ifdef NO_GETGRENT
268                                 gr_rewind();
269 #else
270                                 setgrent();
271 #endif
272                                 continue;
273                         }
274                         fprintf(stderr, _("%s: gid %ld is not unique\n"),
275                                 Prog, (long) group_id);
276                         fail_exit(E_GID_IN_USE);
277                 }
278                 if (! gflg && grp->gr_gid >= group_id) {
279                         if (grp->gr_gid > gid_max)
280                                 continue;
281                         group_id = grp->gr_gid + 1;
282                 }
283         }
284         if (!gflg && group_id == gid_max + 1) {
285                 for (group_id = gid_min; group_id < gid_max; group_id++) {
286 #ifdef NO_GETGRENT
287                         gr_rewind();
288                         while ((grp = gr_next()) && grp->gr_gid != group_id)
289                                 ;
290                         if (!grp)
291                                 break;
292 #else
293                         if (!getgrgid(group_id))
294                                 break;
295 #endif
296                 }
297                 if (group_id == gid_max) {
298                         fprintf(stderr, _("%s: can't get unique gid\n"),
299                                 Prog);
300                         fail_exit(E_GID_IN_USE);
301                 }
302         }
303 }
304
305 /*
306  * check_new_name - check the new name for validity
307  *
308  *      check_new_name() insures that the new name doesn't contain
309  *      any illegal characters.
310  */
311
312 static void
313 check_new_name(void)
314 {
315         if (check_group_name(group_name))
316                 return;
317
318         /*
319          * All invalid group names land here.
320          */
321
322         fprintf(stderr, _("%s: %s is a not a valid group name\n"),
323                 Prog, group_name);
324
325         exit(E_BAD_ARG);
326 }
327
328 /*
329  * process_flags - perform command line argument setting
330  *
331  *      process_flags() interprets the command line arguments and sets
332  *      the values that the user will be created with accordingly.  The
333  *      values are checked for sanity.
334  */
335
336 static void
337 process_flags(int argc, char **argv)
338 {
339         char *cp;
340         int arg;
341
342         while ((arg = getopt(argc, argv, "og:O:f")) != EOF) {
343                 switch (arg) {
344                 case 'g':
345                         gflg++;
346                         if (! isdigit (optarg[0]))
347                                 usage ();
348
349                         group_id = strtol(optarg, &cp, 10);
350                         if (*cp != '\0') {
351                                 fprintf(stderr, _("%s: invalid group %s\n"),
352                                         Prog, optarg);
353                                 fail_exit(E_BAD_ARG);
354                         }
355                         break;
356                 case 'o':
357                         oflg++;
358                         break;
359                 case 'O':
360                         /*
361                          * override login.defs defaults (-O name=value)
362                          * example: -O GID_MIN=100 -O GID_MAX=499
363                          * note: -O GID_MIN=10,GID_MAX=499 doesn't work yet
364                          */
365                         cp = strchr(optarg, '=');
366                         if (!cp) {
367                                 fprintf(stderr,
368                                         _("%s: -O requires NAME=VALUE\n"),
369                                         Prog);
370                                 exit(E_BAD_ARG);
371                         }
372                         /* terminate name, point to value */
373                         *cp++ = '\0';
374                         if (putdef_str(optarg, cp) < 0)
375                                 exit(E_BAD_ARG);
376                         break;
377                 case 'f':
378                         /*
379                          * "force" - do nothing, just exit(0), if the
380                          * specified group already exists.  With -g, if
381                          * specified gid already exists, choose another
382                          * (unique) gid (turn off -g).  Based on the
383                          * RedHat's patch from shadow-utils-970616-9.
384                          */
385                         fflg++;
386                         break;
387                 default:
388                         usage();
389                 }
390         }
391
392         if (oflg && !gflg)
393                 usage();
394
395         if (optind != argc - 1)
396                 usage();
397
398         group_name = argv[argc - 1];
399         check_new_name();
400 }
401
402 /*
403  * close_files - close all of the files that were opened
404  *
405  *      close_files() closes all of the files that were opened for this
406  *      new group.  This causes any modified entries to be written out.
407  */
408
409 static void
410 close_files(void)
411 {
412         if (!gr_close()) {
413                 fprintf(stderr, _("%s: cannot rewrite group file\n"), Prog);
414                 fail_exit(E_GRP_UPDATE);
415         }
416         gr_unlock();
417 #ifdef  SHADOWGRP
418         if (is_shadow_grp && !sgr_close()) {
419                 fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
420                         Prog);
421                 fail_exit(E_GRP_UPDATE);
422         }
423         if (is_shadow_grp)
424                 sgr_unlock ();
425 #endif  /* SHADOWGRP */
426 }
427
428 /*
429  * open_files - lock and open the group files
430  *
431  *      open_files() opens the two group files.
432  */
433
434 static void
435 open_files(void)
436 {
437         if (! gr_lock ()) {
438                 fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
439                 exit(E_GRP_UPDATE);
440         }
441         if (! gr_open (O_RDWR)) {
442                 fprintf(stderr, _("%s: unable to open group file\n"), Prog);
443                 fail_exit(E_GRP_UPDATE);
444         }
445 #ifdef  SHADOWGRP
446         if (is_shadow_grp && ! sgr_lock ()) {
447                 fprintf(stderr, _("%s: unable to lock shadow group file\n"),
448                         Prog);
449                 fail_exit(E_GRP_UPDATE);
450         }
451         if (is_shadow_grp && ! sgr_open (O_RDWR)) {
452                 fprintf(stderr, _("%s: unable to open shadow group file\n"),
453                         Prog);
454                 fail_exit(E_GRP_UPDATE);
455         }
456 #endif  /* SHADOWGRP */
457 }
458
459 /*
460  * fail_exit - exit with an error code after unlocking files
461  */
462
463 static void
464 fail_exit(int code)
465 {
466         (void) gr_unlock ();
467 #ifdef  SHADOWGRP
468         if (is_shadow_grp)
469                 sgr_unlock ();
470 #endif
471         exit (code);
472 }
473
474 /*
475  * main - groupadd command
476  */
477
478 int
479 main(int argc, char **argv)
480 {
481
482         /*
483          * Get my name so that I can use it to report errors.
484          */
485
486         Prog = Basename(argv[0]);
487
488         setlocale(LC_ALL, "");
489         bindtextdomain(PACKAGE, LOCALEDIR);
490         textdomain(PACKAGE);
491
492         openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
493
494 #ifdef SHADOWGRP
495         is_shadow_grp = sgr_file_present();
496 #endif
497
498         /*
499          * The open routines for the DBM files don't use read-write
500          * as the mode, so we have to clue them in.
501          */
502
503 #ifdef  NDBM
504         gr_dbm_mode = O_RDWR;
505 #ifdef  SHADOWGRP
506         sg_dbm_mode = O_RDWR;
507 #endif  /* SHADOWGRP */
508 #endif  /* NDBM */
509         process_flags(argc, argv);
510
511         /*
512          * Start with a quick check to see if the group exists.
513          */
514
515         if (getgrnam(group_name)) {
516                 if (fflg) {
517                         exit(E_SUCCESS);
518                 }
519                 fprintf(stderr, _("%s: group %s exists\n"), Prog, group_name);
520                 exit(E_NAME_IN_USE);
521         }
522
523         /*
524          * Do the hard stuff - open the files, create the group entries,
525          * then close and update the files.
526          */
527
528         open_files();
529
530         if (!gflg || !oflg)
531                 find_new_gid();
532
533         grp_update();
534
535         close_files();
536         exit(E_SUCCESS);
537         /*NOTREACHED*/
538 }