]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/pw/pw_group.c
MFC r322124:
[FreeBSD/stable/10.git] / usr.sbin / pw / pw_group.c
1 /*-
2  * Copyright (C) 1996
3  *      David L. Nugent.  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  *
14  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #ifndef lint
28 static const char rcsid[] =
29   "$FreeBSD$";
30 #endif /* not lint */
31
32 #include <ctype.h>
33 #include <err.h>
34 #include <grp.h>
35 #include <libutil.h>
36 #include <paths.h>
37 #include <string.h>
38 #include <sysexits.h>
39 #include <termios.h>
40 #include <unistd.h>
41
42 #include "pw.h"
43 #include "bitmap.h"
44
45 static struct passwd *lookup_pwent(const char *user);
46 static void     delete_members(struct group *grp, char *list);
47 static int      print_group(struct group * grp, bool pretty);
48 static gid_t    gr_gidpolicy(struct userconf * cnf, intmax_t id);
49
50 static void
51 grp_set_passwd(struct group *grp, bool update, int fd, bool precrypted)
52 {
53         int              b;
54         int              istty;
55         struct termios   t, n;
56         char            *p, line[256];
57
58         if (fd == -1)
59                 return;
60
61         if (fd == '-') {
62                 grp->gr_passwd = "*";   /* No access */
63                 return;
64         }
65         
66         if ((istty = isatty(fd))) {
67                 n = t;
68                 /* Disable echo */
69                 n.c_lflag &= ~(ECHO);
70                 tcsetattr(fd, TCSANOW, &n);
71                 printf("%sassword for group %s:", update ? "New p" : "P",
72                     grp->gr_name);
73                 fflush(stdout);
74         }
75         b = read(fd, line, sizeof(line) - 1);
76         if (istty) {    /* Restore state */
77                 tcsetattr(fd, TCSANOW, &t);
78                 fputc('\n', stdout);
79                 fflush(stdout);
80         }
81         if (b < 0)
82                 err(EX_OSERR, "-h file descriptor");
83         line[b] = '\0';
84         if ((p = strpbrk(line, " \t\r\n")) != NULL)
85                 *p = '\0';
86         if (!*line)
87                 errx(EX_DATAERR, "empty password read on file descriptor %d",
88                     conf.fd);
89         if (precrypted) {
90                 if (strchr(line, ':') != 0)
91                         errx(EX_DATAERR, "wrong encrypted passwrd");
92                 grp->gr_passwd = line;
93         } else
94                 grp->gr_passwd = pw_pwcrypt(line);
95 }
96
97 int
98 pw_groupnext(struct userconf *cnf, bool quiet)
99 {
100         gid_t next = gr_gidpolicy(cnf, -1);
101
102         if (quiet)
103                 return (next);
104         printf("%ju\n", (uintmax_t)next);
105
106         return (EXIT_SUCCESS);
107 }
108
109 static struct group *
110 getgroup(char *name, intmax_t id, bool fatal)
111 {
112         struct group *grp;
113
114         if (id < 0 && name == NULL)
115                 errx(EX_DATAERR, "groupname or id required");
116         grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id);
117         if (grp == NULL) {
118                 if (!fatal)
119                         return (NULL);
120                 if (name == NULL)
121                         errx(EX_DATAERR, "unknown gid `%ju'", id);
122                 errx(EX_DATAERR, "unknown group `%s'", name);
123         }
124         return (grp);
125 }
126
127 /*
128  * Lookup a passwd entry using a name or UID.
129  */
130 static struct passwd *
131 lookup_pwent(const char *user)
132 {
133         struct passwd *pwd;
134
135         if ((pwd = GETPWNAM(user)) == NULL &&
136             (!isdigit((unsigned char)*user) ||
137             (pwd = getpwuid((uid_t) atoi(user))) == NULL))
138                 errx(EX_NOUSER, "user `%s' does not exist", user);
139
140         return (pwd);
141 }
142
143
144 /*
145  * Delete requested members from a group.
146  */
147 static void
148 delete_members(struct group *grp, char *list)
149 {
150         char *p;
151         int k;
152
153         if (grp->gr_mem == NULL)
154                 return;
155
156         for (p = strtok(list, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
157                 for (k = 0; grp->gr_mem[k] != NULL; k++) {
158                         if (strcmp(grp->gr_mem[k], p) == 0)
159                                 break;
160                 }
161                 if (grp->gr_mem[k] == NULL) /* No match */
162                         continue;
163
164                 for (; grp->gr_mem[k] != NULL; k++)
165                         grp->gr_mem[k] = grp->gr_mem[k+1];
166         }
167 }
168
169 static gid_t
170 gr_gidpolicy(struct userconf * cnf, intmax_t id)
171 {
172         struct group   *grp;
173         struct bitmap   bm;
174         gid_t           gid = (gid_t) - 1;
175
176         /*
177          * Check the given gid, if any
178          */
179         if (id > 0) {
180                 gid = (gid_t) id;
181
182                 if ((grp = GETGRGID(gid)) != NULL && conf.checkduplicate)
183                         errx(EX_DATAERR, "gid `%ju' has already been allocated",
184                             (uintmax_t)grp->gr_gid);
185                 return (gid);
186         }
187
188         /*
189          * We need to allocate the next available gid under one of
190          * two policies a) Grab the first unused gid b) Grab the
191          * highest possible unused gid
192          */
193         if (cnf->min_gid >= cnf->max_gid) {     /* Sanity claus^H^H^H^Hheck */
194                 cnf->min_gid = 1000;
195                 cnf->max_gid = 32000;
196         }
197         bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
198
199         /*
200          * Now, let's fill the bitmap from the password file
201          */
202         SETGRENT();
203         while ((grp = GETGRENT()) != NULL)
204                 if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
205                     (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
206                         bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
207         ENDGRENT();
208
209         /*
210          * Then apply the policy, with fallback to reuse if necessary
211          */
212         if (cnf->reuse_gids)
213                 gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
214         else {
215                 gid = (gid_t) (bm_lastset(&bm) + 1);
216                 if (!bm_isset(&bm, gid))
217                         gid += cnf->min_gid;
218                 else
219                         gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
220         }
221
222         /*
223          * Another sanity check
224          */
225         if (gid < cnf->min_gid || gid > cnf->max_gid)
226                 errx(EX_SOFTWARE, "unable to allocate a new gid - range fully "
227                     "used");
228         bm_dealloc(&bm);
229         return (gid);
230 }
231
232 static int
233 print_group(struct group * grp, bool pretty)
234 {
235         char *buf = NULL;
236         int i;
237
238         if (pretty) {
239                 printf("Group Name: %-15s   #%lu\n"
240                        "   Members: ",
241                        grp->gr_name, (long) grp->gr_gid);
242                 if (grp->gr_mem != NULL) {
243                         for (i = 0; grp->gr_mem[i]; i++)
244                                 printf("%s%s", i ? "," : "", grp->gr_mem[i]);
245                 }
246                 fputs("\n\n", stdout);
247                 return (EXIT_SUCCESS);
248         }
249
250         buf = gr_make(grp);
251         printf("%s\n", buf);
252         free(buf);
253         return (EXIT_SUCCESS);
254 }
255
256 int
257 pw_group_next(int argc, char **argv, char *arg1 __unused)
258 {
259         struct userconf *cnf;
260         const char *cfg = NULL;
261         int ch;
262         bool quiet = false;
263
264         while ((ch = getopt(argc, argv, "Cq")) != -1) {
265                 switch (ch) {
266                 case 'C':
267                         cfg = optarg;
268                         break;
269                 case 'q':
270                         quiet = true;
271                         break;
272                 }
273         }
274
275         if (quiet)
276                 freopen(_PATH_DEVNULL, "w", stderr);
277         cnf = get_userconfig(cfg);
278         return (pw_groupnext(cnf, quiet));
279 }
280
281 int
282 pw_group_show(int argc, char **argv, char *arg1)
283 {
284         struct group *grp = NULL;
285         char *name = NULL;
286         intmax_t id = -1;
287         int ch;
288         bool all, force, quiet, pretty;
289
290         all = force = quiet = pretty = false;
291
292         struct group fakegroup = {
293                 "nogroup",
294                 "*",
295                 -1,
296                 NULL
297         };
298
299         if (arg1 != NULL) {
300                 if (arg1[strspn(arg1, "0123456789")] == '\0')
301                         id = pw_checkid(arg1, GID_MAX);
302                 else
303                         name = arg1;
304         }
305
306         while ((ch = getopt(argc, argv, "C:qn:g:FPa")) != -1) {
307                 switch (ch) {
308                 case 'C':
309                         /* ignore compatibility */
310                         break;
311                 case 'q':
312                         quiet = true;
313                         break;
314                 case 'n':
315                         name = optarg;
316                         break;
317                 case 'g':
318                         id = pw_checkid(optarg, GID_MAX);
319                         break;
320                 case 'F':
321                         force = true;
322                         break;
323                 case 'P':
324                         pretty = true;
325                         break;
326                 case 'a':
327                         all = true;
328                         break;
329                 }
330         }
331
332         if (quiet)
333                 freopen(_PATH_DEVNULL, "w", stderr);
334
335         if (all) {
336                 SETGRENT();
337                 while ((grp = GETGRENT()) != NULL)
338                         print_group(grp, pretty);
339                 ENDGRENT();
340                 return (EXIT_SUCCESS);
341         }
342
343         grp = getgroup(name, id, !force);
344         if (grp == NULL)
345                 grp = &fakegroup;
346
347         return (print_group(grp, pretty));
348 }
349
350 int
351 pw_group_del(int argc, char **argv, char *arg1)
352 {
353         struct userconf *cnf = NULL;
354         struct group *grp = NULL;
355         char *name;
356         const char *cfg = NULL;
357         intmax_t id = -1;
358         int ch, rc;
359         bool quiet = false;
360         bool nis = false;
361
362         if (arg1 != NULL) {
363                 if (arg1[strspn(arg1, "0123456789")] == '\0')
364                         id = pw_checkid(arg1, GID_MAX);
365                 else
366                         name = arg1;
367         }
368
369         while ((ch = getopt(argc, argv, "C:qn:g:Y")) != -1) {
370                 switch (ch) {
371                 case 'C':
372                         cfg = optarg;
373                         break;
374                 case 'q':
375                         quiet = true;
376                         break;
377                 case 'n':
378                         name = optarg;
379                         break;
380                 case 'g':
381                         id = pw_checkid(optarg, GID_MAX);
382                         break;
383                 case 'Y':
384                         nis = true;
385                         break;
386                 }
387         }
388
389         if (quiet)
390                 freopen(_PATH_DEVNULL, "w", stderr);
391         grp = getgroup(name, id, true);
392         cnf = get_userconfig(cfg);
393         rc = delgrent(grp);
394         if (rc == -1)
395                 err(EX_IOERR, "group '%s' not available (NIS?)", name);
396         else if (rc != 0)
397                 err(EX_IOERR, "group update");
398         pw_log(cnf, M_DELETE, W_GROUP, "%s(%ju) removed", name,
399             (uintmax_t)id);
400
401         if (nis && nis_update() == 0)
402                 pw_log(cnf, M_DELETE, W_GROUP, "NIS maps updated");
403
404         return (EXIT_SUCCESS);
405 }
406
407 static bool
408 grp_has_member(struct group *grp, const char *name)
409 {
410         int j;
411
412         for (j = 0; grp->gr_mem != NULL && grp->gr_mem[j] != NULL; j++)
413                 if (strcmp(grp->gr_mem[j], name) == 0)
414                         return (true);
415         return (false);
416 }
417
418 static void
419 grp_add_members(struct group **grp, char *members)
420 {
421         struct passwd *pwd;
422         char *p;
423         char tok[] = ", \t";
424
425         if (members == NULL)
426                 return;
427         for (p = strtok(members, tok); p != NULL; p = strtok(NULL, tok)) {
428                 pwd = lookup_pwent(p);
429                 if (grp_has_member(*grp, pwd->pw_name))
430                         continue;
431                 *grp = gr_add(*grp, pwd->pw_name);
432         }
433 }
434
435 int
436 groupadd(struct userconf *cnf, char *name, gid_t id, char *members, int fd,
437     bool dryrun, bool pretty, bool precrypted)
438 {
439         struct group *grp;
440         int rc;
441
442         struct group fakegroup = {
443                 "nogroup",
444                 "*",
445                 -1,
446                 NULL
447         };
448
449         grp = &fakegroup;
450         grp->gr_name = pw_checkname(name, 0);
451         grp->gr_passwd = "*";
452         grp->gr_gid = gr_gidpolicy(cnf, id);
453         grp->gr_mem = NULL;
454
455         /*
456          * This allows us to set a group password Group passwords is an
457          * antique idea, rarely used and insecure (no secure database) Should
458          * be discouraged, but it is apparently still supported by some
459          * software.
460          */
461         grp_set_passwd(grp, false, fd, precrypted);
462         grp_add_members(&grp, members);
463         if (dryrun)
464                 return (print_group(grp, pretty));
465
466         if ((rc = addgrent(grp)) != 0) {
467                 if (rc == -1)
468                         errx(EX_IOERR, "group '%s' already exists",
469                             grp->gr_name);
470                 else
471                         err(EX_IOERR, "group update");
472         }
473
474         pw_log(cnf, M_ADD, W_GROUP, "%s(%ju)", grp->gr_name,
475             (uintmax_t)grp->gr_gid);
476
477         return (EXIT_SUCCESS);
478 }
479
480 int
481 pw_group_add(int argc, char **argv, char *arg1)
482 {
483         struct userconf *cnf = NULL;
484         char *name = NULL;
485         char *members = NULL;
486         const char *cfg = NULL;
487         intmax_t id = -1;
488         int ch, rc, fd = -1;
489         bool quiet, precrypted, dryrun, pretty, nis;
490
491         quiet = precrypted = dryrun = pretty = nis = false;
492
493         if (arg1 != NULL) {
494                 if (arg1[strspn(arg1, "0123456789")] == '\0')
495                         id = pw_checkid(arg1, GID_MAX);
496                 else
497                         name = arg1;
498         }
499
500         while ((ch = getopt(argc, argv, "C:qn:g:h:H:M:oNPY")) != -1) {
501                 switch (ch) {
502                 case 'C':
503                         cfg = optarg;
504                         break;
505                 case 'q':
506                         quiet = true;
507                         break;
508                 case 'n':
509                         name = optarg;
510                         break;
511                 case 'g':
512                         id = pw_checkid(optarg, GID_MAX);
513                         break;
514                 case 'H':
515                         if (fd != -1)
516                                 errx(EX_USAGE, "'-h' and '-H' are mutually "
517                                     "exclusive options");
518                         fd = pw_checkfd(optarg);
519                         precrypted = true;
520                         if (fd == '-')
521                                 errx(EX_USAGE, "-H expects a file descriptor");
522                         break;
523                 case 'h':
524                         if (fd != -1)
525                                 errx(EX_USAGE, "'-h' and '-H' are mutually "
526                                     "exclusive options");
527                         fd = pw_checkfd(optarg);
528                         break;
529                 case 'M':
530                         members = optarg;
531                         break;
532                 case 'o':
533                         conf.checkduplicate = false;
534                         break;
535                 case 'N':
536                         dryrun = true;
537                         break;
538                 case 'P':
539                         pretty = true;
540                         break;
541                 case 'Y':
542                         nis = true;
543                         break;
544                 }
545         }
546
547         if (quiet)
548                 freopen(_PATH_DEVNULL, "w", stderr);
549         if (name == NULL)
550                 errx(EX_DATAERR, "group name required");
551         if (GETGRNAM(name) != NULL)
552                 errx(EX_DATAERR, "group name `%s' already exists", name);
553         cnf = get_userconfig(cfg);
554         rc = groupadd(cnf, name, gr_gidpolicy(cnf, id), members, fd, dryrun,
555             pretty, precrypted);
556         if (nis && rc == EXIT_SUCCESS && nis_update() == 0)
557                 pw_log(cnf, M_ADD, W_GROUP, "NIS maps updated");
558
559         return (rc);
560 }
561
562 int
563 pw_group_mod(int argc, char **argv, char *arg1)
564 {
565         struct userconf *cnf;
566         struct group *grp = NULL;
567         const char *cfg = NULL;
568         char *oldmembers = NULL;
569         char *members = NULL;
570         char *newmembers = NULL;
571         char *newname = NULL;
572         char *name = NULL;
573         intmax_t id = -1;
574         int ch, rc, fd = -1;
575         bool quiet, pretty, dryrun, nis, precrypted;
576
577         quiet = pretty = dryrun = nis = precrypted = false;
578
579         if (arg1 != NULL) {
580                 if (arg1[strspn(arg1, "0123456789")] == '\0')
581                         id = pw_checkid(arg1, GID_MAX);
582                 else
583                         name = arg1;
584         }
585
586         while ((ch = getopt(argc, argv, "C:qn:d:g:l:h:H:M:m:NPY")) != -1) {
587                 switch (ch) {
588                 case 'C':
589                         cfg = optarg;
590                         break;
591                 case 'q':
592                         quiet = true;
593                         break;
594                 case 'n':
595                         name = optarg;
596                         break;
597                 case 'g':
598                         id = pw_checkid(optarg, GID_MAX);
599                         break;
600                 case 'd':
601                         oldmembers = optarg;
602                         break;
603                 case 'l':
604                         newname = optarg;
605                         break;
606                 case 'H':
607                         if (fd != -1)
608                                 errx(EX_USAGE, "'-h' and '-H' are mutually "
609                                     "exclusive options");
610                         fd = pw_checkfd(optarg);
611                         precrypted = true;
612                         if (fd == '-')
613                                 errx(EX_USAGE, "-H expects a file descriptor");
614                         break;
615                 case 'h':
616                         if (fd != -1)
617                                 errx(EX_USAGE, "'-h' and '-H' are mutually "
618                                     "exclusive options");
619                         fd = pw_checkfd(optarg);
620                         break;
621                 case 'M':
622                         members = optarg;
623                         break;
624                 case 'm':
625                         newmembers = optarg;
626                         break;
627                 case 'N':
628                         dryrun = true;
629                         break;
630                 case 'P':
631                         pretty = true;
632                         break;
633                 case 'Y':
634                         nis = true;
635                         break;
636                 }
637         }
638         if (quiet)
639                 freopen(_PATH_DEVNULL, "w", stderr);
640         cnf = get_userconfig(cfg);
641         grp = getgroup(name, id, true);
642         if (name == NULL)
643                 name = grp->gr_name;
644         if (id > 0)
645                 grp->gr_gid = id;
646
647         if (newname != NULL)
648                 grp->gr_name = pw_checkname(newname, 0);
649
650         grp_set_passwd(grp, true, fd, precrypted);
651         /*
652          * Keep the same logic as old code for now:
653          * if -M is passed, -d and -m are ignored
654          * then id -d, -m is ignored
655          * last is -m
656          */
657
658         if (members) {
659                 grp->gr_mem = NULL;
660                 grp_add_members(&grp, members);
661         } else if (oldmembers) {
662                 delete_members(grp, oldmembers);
663         } else if (newmembers) {
664                 grp_add_members(&grp, newmembers);
665         }
666
667         if (dryrun) {
668                 print_group(grp, pretty);
669                 return (EXIT_SUCCESS);
670         }
671
672         if ((rc = chggrent(name, grp)) != 0) {
673                 if (rc == -1)
674                         errx(EX_IOERR, "group '%s' not available (NIS?)",
675                             grp->gr_name);
676                 else
677                         err(EX_IOERR, "group update");
678         }
679
680         if (newname)
681                 name = newname;
682
683         /* grp may have been invalidated */
684         if ((grp = GETGRNAM(name)) == NULL)
685                 errx(EX_SOFTWARE, "group disappeared during update");
686
687         pw_log(cnf, M_UPDATE, W_GROUP, "%s(%ju)", grp->gr_name,
688             (uintmax_t)grp->gr_gid);
689
690         if (nis && nis_update() == 0)
691                 pw_log(cnf, M_UPDATE, W_GROUP, "NIS maps updated");
692
693         return (EXIT_SUCCESS);
694 }