]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - usr.sbin/pw/pw_group.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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         if (members)
276                 free(members);
277
278         return EXIT_SUCCESS;
279 }
280
281
282 /*
283  * Lookup a passwd entry using a name or UID.
284  */
285 static struct passwd *
286 lookup_pwent(const char *user)
287 {
288         struct passwd *pwd;
289
290         if ((pwd = GETPWNAM(user)) == NULL &&
291             (!isdigit((unsigned char)*user) ||
292             (pwd = getpwuid((uid_t) atoi(user))) == NULL))
293                 errx(EX_NOUSER, "user `%s' does not exist", user);
294
295         return (pwd);
296 }
297
298
299 /*
300  * Delete requested members from a group.
301  */
302 static void
303 delete_members(char ***members, int *grmembers, int *i, struct carg *arg,
304     struct group *grp)
305 {
306         bool matchFound;
307         char *user;
308         char *valueCopy;
309         char *valuePtr;
310         int k;
311         struct passwd *pwd;
312
313         k = 0;
314         while (grp->gr_mem[k] != NULL) {
315                 matchFound = false;
316                 if ((valueCopy = strdup(arg->val)) == NULL)
317                         errx(EX_UNAVAILABLE, "out of memory");
318                 valuePtr = valueCopy;
319                 while ((user = strsep(&valuePtr, ", \t")) != NULL) {
320                         pwd = lookup_pwent(user);
321                         if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) {
322                                 matchFound = true;
323                                 break;
324                         }
325                 }
326                 free(valueCopy);
327
328                 if (!matchFound &&
329                     extendarray(members, grmembers, *i + 2) != -1)
330                         (*members)[(*i)++] = grp->gr_mem[k];
331
332                 k++;
333         }
334
335         return;
336 }
337
338
339 static          gid_t
340 gr_gidpolicy(struct userconf * cnf, struct cargs * args)
341 {
342         struct group   *grp;
343         gid_t           gid = (gid_t) - 1;
344         struct carg    *a_gid = getarg(args, 'g');
345
346         /*
347          * Check the given gid, if any
348          */
349         if (a_gid != NULL) {
350                 gid = (gid_t) atol(a_gid->val);
351
352                 if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL)
353                         errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
354         } else {
355                 struct bitmap   bm;
356
357                 /*
358                  * We need to allocate the next available gid under one of
359                  * two policies a) Grab the first unused gid b) Grab the
360                  * highest possible unused gid
361                  */
362                 if (cnf->min_gid >= cnf->max_gid) {     /* Sanity claus^H^H^H^Hheck */
363                         cnf->min_gid = 1000;
364                         cnf->max_gid = 32000;
365                 }
366                 bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
367
368                 /*
369                  * Now, let's fill the bitmap from the password file
370                  */
371                 SETGRENT();
372                 while ((grp = GETGRENT()) != NULL)
373                         if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
374                             (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
375                                 bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
376                 ENDGRENT();
377
378                 /*
379                  * Then apply the policy, with fallback to reuse if necessary
380                  */
381                 if (cnf->reuse_gids)
382                         gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
383                 else {
384                         gid = (gid_t) (bm_lastset(&bm) + 1);
385                         if (!bm_isset(&bm, gid))
386                                 gid += cnf->min_gid;
387                         else
388                                 gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
389                 }
390
391                 /*
392                  * Another sanity check
393                  */
394                 if (gid < cnf->min_gid || gid > cnf->max_gid)
395                         errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
396                 bm_dealloc(&bm);
397         }
398         return gid;
399 }
400
401
402 static int
403 print_group(struct group * grp, int pretty)
404 {
405         if (!pretty) {
406                 int             buflen = 0;
407                 char           *buf = NULL;
408
409                 fmtgrent(&buf, &buflen, grp);
410                 fputs(buf, stdout);
411                 free(buf);
412         } else {
413                 int             i;
414
415                 printf("Group Name: %-15s   #%lu\n"
416                        "   Members: ",
417                        grp->gr_name, (long) grp->gr_gid);
418                 for (i = 0; grp->gr_mem[i]; i++)
419                         printf("%s%s", i ? "," : "", grp->gr_mem[i]);
420                 fputs("\n\n", stdout);
421         }
422         return EXIT_SUCCESS;
423 }