]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/pw/pw_group.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 <termios.h>
35 #include <stdbool.h>
36 #include <unistd.h>
37
38 #include "pw.h"
39 #include "bitmap.h"
40
41
42 static struct passwd *lookup_pwent(const char *user);
43 static void     delete_members(char ***members, int *grmembers, int *i,
44     struct carg *arg, struct group *grp);
45 static int      print_group(struct group * grp, int pretty);
46 static gid_t    gr_gidpolicy(struct userconf * cnf, struct cargs * args);
47
48 int
49 pw_group(struct userconf * cnf, int mode, struct cargs * args)
50 {
51         int             rc;
52         struct carg    *a_name = getarg(args, 'n');
53         struct carg    *a_gid = getarg(args, 'g');
54         struct carg    *arg;
55         struct group   *grp = NULL;
56         int             grmembers = 0;
57         char           **members = NULL;
58
59         static struct group fakegroup =
60         {
61                 "nogroup",
62                 "*",
63                 -1,
64                 NULL
65         };
66
67         if (mode == M_LOCK || mode == M_UNLOCK)
68                 errx(EX_USAGE, "'lock' command is not available for groups");
69
70         /*
71          * With M_NEXT, we only need to return the
72          * next gid to stdout
73          */
74         if (mode == M_NEXT) {
75                 gid_t next = gr_gidpolicy(cnf, args);
76                 if (getarg(args, 'q'))
77                         return next;
78                 printf("%ld\n", (long)next);
79                 return EXIT_SUCCESS;
80         }
81
82         if (mode == M_PRINT && getarg(args, 'a')) {
83                 int             pretty = getarg(args, 'P') != NULL;
84
85                 SETGRENT();
86                 while ((grp = GETGRENT()) != NULL)
87                         print_group(grp, pretty);
88                 ENDGRENT();
89                 return EXIT_SUCCESS;
90         }
91         if (a_gid == NULL) {
92                 if (a_name == NULL)
93                         errx(EX_DATAERR, "group name or id required");
94
95                 if (mode != M_ADD && grp == NULL && isdigit((unsigned char)*a_name->val)) {
96                         (a_gid = a_name)->ch = 'g';
97                         a_name = NULL;
98                 }
99         }
100         grp = (a_name != NULL) ? GETGRNAM(a_name->val) : GETGRGID((gid_t) atoi(a_gid->val));
101
102         if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
103                 if (a_name == NULL && grp == NULL)      /* Try harder */
104                         grp = GETGRGID(atoi(a_gid->val));
105
106                 if (grp == NULL) {
107                         if (mode == M_PRINT && getarg(args, 'F')) {
108                                 char    *fmems[1];
109                                 fmems[0] = NULL;
110                                 fakegroup.gr_name = a_name ? a_name->val : "nogroup";
111                                 fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
112                                 fakegroup.gr_mem = fmems;
113                                 return print_group(&fakegroup, getarg(args, 'P') != NULL);
114                         }
115                         errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val);
116                 }
117                 if (a_name == NULL)     /* Needed later */
118                         a_name = addarg(args, 'n', grp->gr_name);
119
120                 /*
121                  * Handle deletions now
122                  */
123                 if (mode == M_DELETE) {
124                         gid_t           gid = grp->gr_gid;
125
126                         rc = delgrent(grp);
127                         if (rc == -1)
128                                 err(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name);
129                         else if (rc != 0) {
130                                 warn("group update");
131                                 return EX_IOERR;
132                         }
133                         pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
134                         return EXIT_SUCCESS;
135                 } else if (mode == M_PRINT)
136                         return print_group(grp, getarg(args, 'P') != NULL);
137
138                 if (a_gid)
139                         grp->gr_gid = (gid_t) atoi(a_gid->val);
140
141                 if ((arg = getarg(args, 'l')) != NULL)
142                         grp->gr_name = pw_checkname((u_char *)arg->val, 0);
143         } else {
144                 if (a_name == NULL)     /* Required */
145                         errx(EX_DATAERR, "group name required");
146                 else if (grp != NULL)   /* Exists */
147                         errx(EX_DATAERR, "group name `%s' already exists", a_name->val);
148
149                 extendarray(&members, &grmembers, 200);
150                 members[0] = NULL;
151                 grp = &fakegroup;
152                 grp->gr_name = pw_checkname((u_char *)a_name->val, 0);
153                 grp->gr_passwd = "*";
154                 grp->gr_gid = gr_gidpolicy(cnf, args);
155                 grp->gr_mem = members;
156         }
157
158         /*
159          * This allows us to set a group password Group passwords is an
160          * antique idea, rarely used and insecure (no secure database) Should
161          * be discouraged, but it is apparently still supported by some
162          * software.
163          */
164
165         if ((arg = getarg(args, 'h')) != NULL ||
166             (arg = getarg(args, 'H')) != NULL) {
167                 if (strcmp(arg->val, "-") == 0)
168                         grp->gr_passwd = "*";   /* No access */
169                 else {
170                         int             fd = atoi(arg->val);
171                         int             precrypt = (arg->ch == 'H');
172                         int             b;
173                         int             istty = isatty(fd);
174                         struct termios  t;
175                         char           *p, line[256];
176
177                         if (istty) {
178                                 if (tcgetattr(fd, &t) == -1)
179                                         istty = 0;
180                                 else {
181                                         struct termios  n = t;
182
183                                         /* Disable echo */
184                                         n.c_lflag &= ~(ECHO);
185                                         tcsetattr(fd, TCSANOW, &n);
186                                         printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
187                                         fflush(stdout);
188                                 }
189                         }
190                         b = read(fd, line, sizeof(line) - 1);
191                         if (istty) {    /* Restore state */
192                                 tcsetattr(fd, TCSANOW, &t);
193                                 fputc('\n', stdout);
194                                 fflush(stdout);
195                         }
196                         if (b < 0) {
197                                 warn("-h file descriptor");
198                                 return EX_OSERR;
199                         }
200                         line[b] = '\0';
201                         if ((p = strpbrk(line, " \t\r\n")) != NULL)
202                                 *p = '\0';
203                         if (!*line)
204                                 errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
205                         if (precrypt) {
206                                 if (strchr(line, ':') != NULL)
207                                         return EX_DATAERR;
208                                 grp->gr_passwd = line;
209                         } else
210                                 grp->gr_passwd = pw_pwcrypt(line);
211                 }
212         }
213
214         if (((arg = getarg(args, 'M')) != NULL ||
215             (arg = getarg(args, 'd')) != NULL ||
216             (arg = getarg(args, 'm')) != NULL) && arg->val) {
217                 int     i = 0;
218                 char   *p;
219                 struct passwd   *pwd;
220
221                 /* Make sure this is not stay NULL with -M "" */
222                 extendarray(&members, &grmembers, 200);
223                 if (arg->ch == 'd')
224                         delete_members(&members, &grmembers, &i, arg, grp);
225                 else if (arg->ch == 'm') {
226                         int     k = 0;
227
228                         while (grp->gr_mem[k] != NULL) {
229                                 if (extendarray(&members, &grmembers, i + 2) != -1)
230                                         members[i++] = grp->gr_mem[k];
231                                 k++;
232                         }
233                 }
234
235                 if (arg->ch != 'd')
236                         for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
237                                 int     j;
238
239                                 /*
240                                  * Check for duplicates
241                                  */
242                                 pwd = lookup_pwent(p);
243                                 for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++)
244                                         ;
245                                 if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
246                                         members[i++] = newstr(pwd->pw_name);
247                         }
248                 while (i < grmembers)
249                         members[i++] = NULL;
250                 grp->gr_mem = members;
251         }
252
253         if (getarg(args, 'N') != NULL)
254                 return print_group(grp, getarg(args, 'P') != NULL);
255
256         if (mode == M_ADD && (rc = addgrent(grp)) != 0) {
257                 if (rc == -1)
258                         warnx("group '%s' already exists", grp->gr_name);
259                 else
260                         warn("group update");
261                 return EX_IOERR;
262         } else if (mode == M_UPDATE && (rc = chggrent(a_name->val, grp)) != 0) {
263                 if (rc == -1)
264                         warnx("group '%s' not available (NIS?)", grp->gr_name);
265                 else
266                         warn("group update");
267                 return EX_IOERR;
268         }
269         /* grp may have been invalidated */
270         if ((grp = GETGRNAM(a_name->val)) == NULL)
271                 errx(EX_SOFTWARE, "group disappeared during update");
272
273         pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
274
275         free(members);
276
277         return EXIT_SUCCESS;
278 }
279
280
281 /*
282  * Lookup a passwd entry using a name or UID.
283  */
284 static struct passwd *
285 lookup_pwent(const char *user)
286 {
287         struct passwd *pwd;
288
289         if ((pwd = GETPWNAM(user)) == NULL &&
290             (!isdigit((unsigned char)*user) ||
291             (pwd = getpwuid((uid_t) atoi(user))) == NULL))
292                 errx(EX_NOUSER, "user `%s' does not exist", user);
293
294         return (pwd);
295 }
296
297
298 /*
299  * Delete requested members from a group.
300  */
301 static void
302 delete_members(char ***members, int *grmembers, int *i, struct carg *arg,
303     struct group *grp)
304 {
305         bool matchFound;
306         char *user;
307         char *valueCopy;
308         char *valuePtr;
309         int k;
310         struct passwd *pwd;
311
312         k = 0;
313         while (grp->gr_mem[k] != NULL) {
314                 matchFound = false;
315                 if ((valueCopy = strdup(arg->val)) == NULL)
316                         errx(EX_UNAVAILABLE, "out of memory");
317                 valuePtr = valueCopy;
318                 while ((user = strsep(&valuePtr, ", \t")) != NULL) {
319                         pwd = lookup_pwent(user);
320                         if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) {
321                                 matchFound = true;
322                                 break;
323                         }
324                 }
325                 free(valueCopy);
326
327                 if (!matchFound &&
328                     extendarray(members, grmembers, *i + 2) != -1)
329                         (*members)[(*i)++] = grp->gr_mem[k];
330
331                 k++;
332         }
333
334         return;
335 }
336
337
338 static          gid_t
339 gr_gidpolicy(struct userconf * cnf, struct cargs * args)
340 {
341         struct group   *grp;
342         gid_t           gid = (gid_t) - 1;
343         struct carg    *a_gid = getarg(args, 'g');
344
345         /*
346          * Check the given gid, if any
347          */
348         if (a_gid != NULL) {
349                 gid = (gid_t) atol(a_gid->val);
350
351                 if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL)
352                         errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
353         } else {
354                 struct bitmap   bm;
355
356                 /*
357                  * We need to allocate the next available gid under one of
358                  * two policies a) Grab the first unused gid b) Grab the
359                  * highest possible unused gid
360                  */
361                 if (cnf->min_gid >= cnf->max_gid) {     /* Sanity claus^H^H^H^Hheck */
362                         cnf->min_gid = 1000;
363                         cnf->max_gid = 32000;
364                 }
365                 bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
366
367                 /*
368                  * Now, let's fill the bitmap from the password file
369                  */
370                 SETGRENT();
371                 while ((grp = GETGRENT()) != NULL)
372                         if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
373                             (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
374                                 bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
375                 ENDGRENT();
376
377                 /*
378                  * Then apply the policy, with fallback to reuse if necessary
379                  */
380                 if (cnf->reuse_gids)
381                         gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
382                 else {
383                         gid = (gid_t) (bm_lastset(&bm) + 1);
384                         if (!bm_isset(&bm, gid))
385                                 gid += cnf->min_gid;
386                         else
387                                 gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
388                 }
389
390                 /*
391                  * Another sanity check
392                  */
393                 if (gid < cnf->min_gid || gid > cnf->max_gid)
394                         errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
395                 bm_dealloc(&bm);
396         }
397         return gid;
398 }
399
400
401 static int
402 print_group(struct group * grp, int pretty)
403 {
404         if (!pretty) {
405                 int             buflen = 0;
406                 char           *buf = NULL;
407
408                 fmtgrent(&buf, &buflen, grp);
409                 fputs(buf, stdout);
410                 free(buf);
411         } else {
412                 int             i;
413
414                 printf("Group Name: %-15s   #%lu\n"
415                        "   Members: ",
416                        grp->gr_name, (long) grp->gr_gid);
417                 for (i = 0; grp->gr_mem[i]; i++)
418                         printf("%s%s", i ? "," : "", grp->gr_mem[i]);
419                 fputs("\n\n", stdout);
420         }
421         return EXIT_SUCCESS;
422 }