]> granicus.if.org Git - shadow/blob - src/groupmod.c
[svn-upgrade] Integrating new upstream version, shadow (20001012)
[shadow] / src / groupmod.c
1 /*
2  * Copyright 1991 - 1994, 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: groupmod.c,v 1.16 2000/10/09 19:02:20 kloczek 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 #ifdef USE_PAM
42 #include <security/pam_appl.h>
43 #include <security/pam_misc.h>
44 #include <pwd.h>
45 #endif /* USE_PAM */
46
47 #include "prototypes.h"
48 #include "chkname.h"
49 #include "defines.h"
50
51 #include "groupio.h"
52
53 #ifdef  SHADOWGRP
54 #include "sgroupio.h"
55
56 static int is_shadow_grp;
57 #endif
58
59 /*
60  * exit status values
61  */
62
63 #define E_SUCCESS       0       /* success */
64 #define E_USAGE         2       /* invalid command syntax */
65 #define E_BAD_ARG       3       /* invalid argument to option */
66 #define E_GID_IN_USE    4       /* gid already in use (and no -o) */
67 #define E_NOTFOUND      6       /* specified group doesn't exist */
68 #define E_NAME_IN_USE   9       /* group name already in use */
69 #define E_GRP_UPDATE    10      /* can't update group file */
70
71 static char     *group_name;
72 static char     *group_newname;
73 static gid_t    group_id;
74 static gid_t    group_newid;
75
76 static char     *Prog;
77
78 static int
79         oflg = 0, /* permit non-unique group ID to be specified with -g */
80         gflg = 0, /* new ID value for the group */
81         nflg = 0; /* a new name has been specified for the group */
82
83 #ifdef  NDBM
84 extern  int     gr_dbm_mode;
85 extern  int     sg_dbm_mode;
86 #endif
87
88 extern int optind;
89 extern char *optarg;
90
91 /* local function prototypes */
92 static void usage(void);
93 static void new_grent(struct group *);
94 #ifdef SHADOWGRP
95 static void new_sgent(struct sgrp *);
96 #endif
97 static void grp_update(void);
98 static void check_new_gid(void);
99 static void check_new_name(void);
100 static void process_flags(int, char **);
101 static void close_files(void);
102 static void open_files(void);
103
104 /*
105  * usage - display usage message and exit
106  */
107
108 static void
109 usage(void)
110 {
111         fprintf(stderr, _("usage: groupmod [-g gid [-o]] [-n name] group\n"));
112         exit(E_USAGE);
113 }
114
115 /*
116  * new_grent - updates the values in a group file entry
117  *
118  *      new_grent() takes all of the values that have been entered and
119  *      fills in a (struct group) with them.
120  */
121
122 static void
123 new_grent(struct group *grent)
124 {
125         if (nflg)
126                 grent->gr_name = xstrdup (group_newname);
127
128         if (gflg)
129                 grent->gr_gid = group_newid;
130 }
131
132 #ifdef  SHADOWGRP
133 /*
134  * new_sgent - updates the values in a shadow group file entry
135  *
136  *      new_sgent() takes all of the values that have been entered and
137  *      fills in a (struct sgrp) with them.
138  */
139
140 static void
141 new_sgent(struct sgrp *sgent)
142 {
143         if (nflg)
144                 sgent->sg_name = xstrdup (group_newname);
145 }
146 #endif  /* SHADOWGRP */
147
148 /*
149  * grp_update - update group file entries
150  *
151  *      grp_update() writes the new records to the group files.
152  */
153
154 static void
155 grp_update(void)
156 {
157         struct group grp;
158         const struct group *ogrp;
159 #ifdef  SHADOWGRP
160         struct sgrp sgrp;
161         const struct sgrp *osgrp = NULL;
162 #endif  /* SHADOWGRP */
163
164         /*
165          * Get the current settings for this group.
166          */
167
168         ogrp = gr_locate(group_name);
169         if (!ogrp) {
170                 fprintf(stderr,
171                         _("%s: %s not found in /etc/group\n"),
172                         Prog, group_name);
173                 exit(E_GRP_UPDATE);
174         }
175         grp = *ogrp;
176         new_grent (&grp);
177 #ifdef  SHADOWGRP
178         if (is_shadow_grp && (osgrp = sgr_locate(group_name))) {
179                 sgrp = *osgrp;
180                 new_sgent (&sgrp);
181         }
182 #endif  /* SHADOWGRP */
183
184         /*
185          * Write out the new group file entry.
186          */
187
188         if (!gr_update(&grp)) {
189                 fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
190                 exit(E_GRP_UPDATE);
191         }
192         if (nflg && !gr_remove(group_name)) {
193                 fprintf(stderr, _("%s: error removing group entry\n"), Prog);
194                 exit(E_GRP_UPDATE);
195         }
196 #ifdef  NDBM
197
198         /*
199          * Update the DBM group file with the new entry as well.
200          */
201
202         if (gr_dbm_present()) {
203                 if (!gr_dbm_update(&grp)) {
204                         fprintf(stderr,
205                                 _("%s: cannot add new dbm group entry\n"),
206                                 Prog);
207                         exit(E_GRP_UPDATE);
208                 }
209                 if (nflg && (ogrp = getgrnam(group_name)) &&
210                                 !gr_dbm_remove(ogrp)) {
211                         fprintf(stderr,
212                                 _("%s: error removing group dbm entry\n"),
213                                 Prog);
214                         exit(E_GRP_UPDATE);
215                 }
216                 endgrent ();
217         }
218 #endif  /* NDBM */
219
220 #ifdef  SHADOWGRP
221
222         /*
223          * Make sure there was a shadow entry to begin with.  Skip
224          * down to "out" if there wasn't.  Can't just return because
225          * there might be some syslogging to do.
226          */
227
228         if (! osgrp)
229                 goto out;
230
231         /*
232          * Write out the new shadow group entries as well.
233          */
234
235         if (!sgr_update(&sgrp)) {
236                 fprintf(stderr, _("%s: error adding new group entry\n"), Prog);
237                 exit(E_GRP_UPDATE);
238         }
239         if (nflg && !sgr_remove(group_name)) {
240                 fprintf(stderr, _("%s: error removing group entry\n"), Prog);
241                 exit(E_GRP_UPDATE);
242         }
243 #ifdef  NDBM
244
245         /*
246          * Update the DBM shadow group file with the new entry as well.
247          */
248
249         if (sg_dbm_present()) {
250                 if (!sg_dbm_update(&sgrp)) {
251                         fprintf(stderr,
252                                 _("%s: cannot add new dbm shadow group entry\n"),
253                                 Prog);
254                         exit(E_GRP_UPDATE);
255                 }
256                 if (nflg && ! sg_dbm_remove (group_name)) {
257                         fprintf (stderr,
258                                 _("%s: error removing shadow group dbm entry\n"),
259                                 Prog);
260                         exit(E_GRP_UPDATE);
261                 }
262                 endsgent ();
263         }
264 #endif  /* NDBM */
265 out:
266 #endif  /* SHADOWGRP */
267
268         if (nflg)
269                 SYSLOG((LOG_INFO, "change group `%s' to `%s'\n",
270                         group_name, group_newname));
271
272         if (gflg)
273                 SYSLOG((LOG_INFO, "change gid for `%s' to %d\n",
274                         nflg ? group_newname:group_name, group_newid));
275 }
276
277 /*
278  * check_new_gid - check the new GID value for uniqueness
279  *
280  *      check_new_gid() insures that the new GID value is unique.
281  */
282
283 static void
284 check_new_gid(void)
285 {
286         /*
287          * First, the easy stuff.  If the ID can be duplicated, or if
288          * the ID didn't really change, just return.  If the ID didn't
289          * change, turn off those flags.  No sense doing needless work.
290          */
291
292         if (group_id == group_newid) {
293                 gflg = 0;
294                 return;
295         }
296
297         if (oflg || ! getgrgid (group_newid))
298                 return;
299
300         /*
301          * Tell the user what they did wrong.
302          */
303
304         fprintf(stderr,
305                 _("%s: %ld is not a unique gid\n"),
306                 Prog, (long) group_newid);
307         exit(E_GID_IN_USE);
308 }
309
310 /*
311  * check_new_name - check the new name for uniqueness
312  *
313  *      check_new_name() insures that the new name does not exist
314  *      already.  You can't have the same name twice, period.
315  */
316
317 static void
318 check_new_name(void)
319 {
320         /*
321          * Make sure they are actually changing the name.
322          */
323
324         if (strcmp(group_name, group_newname) == 0) {
325                 nflg = 0;
326                 return;
327         }
328
329         if (check_group_name(group_newname)) {
330
331                 /*
332                  * If the entry is found, too bad.
333                  */
334
335                 if (getgrnam(group_newname)) {
336                         fprintf(stderr, _("%s: %s is not a unique name\n"),
337                                 Prog, group_newname);
338                         exit(E_NAME_IN_USE);
339                 }
340                 return;
341         }
342
343         /*
344          * All invalid group names land here.
345          */
346
347         fprintf(stderr, _("%s: %s is a not a valid group name\n"),
348                 Prog, group_newname);
349         exit(E_BAD_ARG);
350 }
351
352 /*
353  * process_flags - perform command line argument setting
354  *
355  *      process_flags() interprets the command line arguments and sets
356  *      the values that the user will be created with accordingly.  The
357  *      values are checked for sanity.
358  */
359
360 static void
361 process_flags(int argc, char **argv)
362 {
363         char    *end;
364         int     arg;
365
366         while ((arg = getopt (argc, argv, "og:n:")) != EOF) {
367                 switch (arg) {
368                         case 'g':
369                                 gflg++;
370                                 group_newid = strtol(optarg, &end, 10);
371                                 if (*end != '\0') {
372                                         fprintf(stderr,
373                                                 _("%s: invalid group %s\n"),
374                                                 Prog, optarg);
375                                         exit(E_BAD_ARG);
376                                 }
377                                 break;
378                         case 'n':
379                                 nflg++;
380                                 group_newname = optarg;
381                                 break;
382                         case 'o':
383                                 oflg++;
384                                 break;
385                         default:
386                                 usage ();
387                 }
388         }
389         if (oflg && !gflg)
390                 usage();
391
392         if (optind != argc - 1)
393                 usage();
394
395         group_name = argv[argc - 1];
396 }
397
398 /*
399  * close_files - close all of the files that were opened
400  *
401  *      close_files() closes all of the files that were opened for this
402  *      new group.  This causes any modified entries to be written out.
403  */
404
405 static void
406 close_files(void)
407 {
408         if (!gr_close()) {
409                 fprintf(stderr, _("%s: cannot rewrite group file\n"), Prog);
410                 exit(E_GRP_UPDATE);
411         }
412         gr_unlock();
413 #ifdef  SHADOWGRP
414         if (is_shadow_grp && !sgr_close()) {
415                 fprintf(stderr, _("%s: cannot rewrite shadow group file\n"),
416                         Prog);
417                 exit(E_GRP_UPDATE);
418         }
419         if (is_shadow_grp)
420                 sgr_unlock ();
421 #endif  /* SHADOWGRP */
422 }
423
424 /*
425  * open_files - lock and open the group files
426  *
427  *      open_files() opens the two group files.
428  */
429
430 static void
431 open_files(void)
432 {
433         if (!gr_lock()) {
434                 fprintf(stderr, _("%s: unable to lock group file\n"), Prog);
435                 exit(E_GRP_UPDATE);
436         }
437         if (!gr_open(O_RDWR)) {
438                 fprintf(stderr, _("%s: unable to open group file\n"), Prog);
439                 exit(E_GRP_UPDATE);
440         }
441 #ifdef  SHADOWGRP
442         if (is_shadow_grp && !sgr_lock()) {
443                 fprintf(stderr, _("%s: unable to lock shadow group file\n"),
444                         Prog);
445                 exit(E_GRP_UPDATE);
446         }
447         if (is_shadow_grp && !sgr_open(O_RDWR)) {
448                 fprintf(stderr, _("%s: unable to open shadow group file\n"),
449                         Prog);
450                 exit(E_GRP_UPDATE);
451         }
452 #endif  /* SHADOWGRP */
453 }
454
455 #ifdef USE_PAM
456 static struct pam_conv conv = {
457     misc_conv,
458     NULL
459 };
460 #endif /* USE_PAM */
461
462 /*
463  * main - groupmod command
464  *
465  *      The syntax of the groupmod command is
466  *      
467  *      groupmod [ -g gid [ -o ]] [ -n name ] group
468  *
469  *      The flags are
470  *              -g - specify a new group ID value
471  *              -o - permit the group ID value to be non-unique
472  *              -n - specify a new group name
473  */
474
475 int
476 main(int argc, char **argv)
477 {
478         struct  group   *grp;
479 #ifdef USE_PAM
480         pam_handle_t *pamh = NULL;
481         struct passwd *pampw;
482         int retval;
483 #endif
484
485         /*
486          * Get my name so that I can use it to report errors.
487          */
488
489         Prog = Basename(argv[0]);
490
491         setlocale(LC_ALL, "");
492         bindtextdomain(PACKAGE, LOCALEDIR);
493         textdomain(PACKAGE);
494
495 #ifdef USE_PAM
496         retval = PAM_SUCCESS;
497
498         pampw = getpwuid(getuid());
499         if (pampw == NULL) {
500                 retval = PAM_USER_UNKNOWN;
501         }
502
503         if (retval == PAM_SUCCESS) {
504                 retval = pam_start("shadow", pampw->pw_name, &conv, &pamh);
505         }
506
507         if (retval == PAM_SUCCESS) {
508                 retval = pam_authenticate(pamh, 0);
509                 if (retval != PAM_SUCCESS) {
510                         pam_end(pamh, retval);
511                 }
512         }
513
514         if (retval == PAM_SUCCESS) {
515                 retval = pam_acct_mgmt(pamh, 0);
516                 if (retval != PAM_SUCCESS) {
517                         pam_end(pamh, retval);
518                 }
519         }
520
521         if (retval != PAM_SUCCESS) {
522                 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
523                 exit (1);
524         }
525 #endif /* USE_PAM */
526
527         OPENLOG(Prog);
528
529 #ifdef SHADOWGRP
530         is_shadow_grp = sgr_file_present();
531 #endif
532
533         /*
534          * The open routines for the DBM files don't use read-write
535          * as the mode, so we have to clue them in.
536          */
537
538 #ifdef  NDBM
539         gr_dbm_mode = O_RDWR;
540 #ifdef  SHADOWGRP
541         sg_dbm_mode = O_RDWR;
542 #endif  /* SHADOWGRP */
543 #endif  /* NDBM */
544         process_flags (argc, argv);
545
546         /*
547          * Start with a quick check to see if the group exists.
548          */
549
550         if (!(grp = getgrnam(group_name))) {
551                 fprintf(stderr, _("%s: group %s does not exist\n"),
552                         Prog, group_name);
553                 exit(E_NOTFOUND);
554         } else
555                 group_id = grp->gr_gid;
556
557 #ifdef  USE_NIS
558
559         /*
560          * Now make sure it isn't an NIS group.
561          */
562
563         if (__isgrNIS ()) {
564                 char    *nis_domain;
565                 char    *nis_master;
566
567                 fprintf(stderr, _("%s: group %s is a NIS group\n"),
568                         Prog, group_name);
569
570                 if (! yp_get_default_domain (&nis_domain) &&
571                                 ! yp_master (nis_domain, "group.byname",
572                                 &nis_master)) {
573                         fprintf(stderr, _("%s: %s is the NIS master\n"),
574                                 Prog, nis_master);
575                 }
576                 exit(E_NOTFOUND);
577         }
578 #endif
579
580         if (gflg)
581                 check_new_gid ();
582
583         if (nflg)
584                 check_new_name ();
585
586         /*
587          * Do the hard stuff - open the files, create the group entries,
588          * then close and update the files.
589          */
590
591         open_files ();
592
593         grp_update ();
594
595         close_files ();
596
597 #ifdef USE_PAM
598         if (retval == PAM_SUCCESS) {
599                 retval = pam_chauthtok(pamh, 0);
600                 if (retval != PAM_SUCCESS) {
601                         pam_end(pamh, retval);
602                 }
603         }
604
605         if (retval != PAM_SUCCESS) {
606                 fprintf (stderr, _("%s: PAM chauthtok failed\n"), Prog);
607                 exit (1);
608         }
609
610         if (retval == PAM_SUCCESS)
611                 pam_end(pamh, PAM_SUCCESS);
612 #endif /* USE_PAM */
613         exit(E_SUCCESS);
614         /*NOTREACHED*/
615 }