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