]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_group.c
Refactor input validation
[pw-darwin.git] / 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 #include <grp.h>
38 #include <libutil.h>
39
40 #include "pw.h"
41 #include "bitmap.h"
42
43
44 static struct passwd *lookup_pwent(const char *user);
45 static void delete_members(char ***members, int *grmembers, int *i,
46 struct carg *arg, struct group *grp);
47 static int print_group(struct group * grp);
48 static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
49
50 int
51 pw_group(int mode, char *name, long id, struct cargs * args)
52 {
53 int rc;
54 struct carg *a_newname = getarg(args, 'l');
55 struct carg *arg;
56 struct group *grp = NULL;
57 int grmembers = 0;
58 char **members = NULL;
59 struct userconf *cnf = conf.userconf;
60
61 static struct group fakegroup =
62 {
63 "nogroup",
64 "*",
65 -1,
66 NULL
67 };
68
69 if (mode == M_LOCK || mode == M_UNLOCK)
70 errx(EX_USAGE, "'lock' command is not available for groups");
71
72 /*
73 * With M_NEXT, we only need to return the
74 * next gid to stdout
75 */
76 if (mode == M_NEXT) {
77 gid_t next = gr_gidpolicy(cnf, args);
78 if (getarg(args, 'q'))
79 return next;
80 printf("%u\n", next);
81 return EXIT_SUCCESS;
82 }
83
84 if (mode == M_PRINT && getarg(args, 'a')) {
85 SETGRENT();
86 while ((grp = GETGRENT()) != NULL)
87 print_group(grp);
88 ENDGRENT();
89 return EXIT_SUCCESS;
90 }
91 if (id < 0 && name == NULL)
92 errx(EX_DATAERR, "group name or id required");
93
94 grp = (name != NULL) ? GETGRNAM(name) : GETGRGID(id);
95
96 if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
97 if (name == NULL && grp == NULL) /* Try harder */
98 grp = GETGRGID(id);
99
100 if (grp == NULL) {
101 if (mode == M_PRINT && getarg(args, 'F')) {
102 char *fmems[1];
103 fmems[0] = NULL;
104 fakegroup.gr_name = name ? name : "nogroup";
105 fakegroup.gr_gid = (gid_t) id;
106 fakegroup.gr_mem = fmems;
107 return print_group(&fakegroup);
108 }
109 if (name == NULL)
110 errx(EX_DATAERR, "unknown group `%s'", name);
111 else
112 errx(EX_DATAERR, "unknown group `%ld'", id);
113 }
114 if (name == NULL) /* Needed later */
115 name = grp->gr_name;
116
117 /*
118 * Handle deletions now
119 */
120 if (mode == M_DELETE) {
121 rc = delgrent(grp);
122 if (rc == -1)
123 err(EX_IOERR, "group '%s' not available (NIS?)",
124 name);
125 else if (rc != 0) {
126 err(EX_IOERR, "group update");
127 }
128 pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", name, id);
129 return EXIT_SUCCESS;
130 } else if (mode == M_PRINT)
131 return print_group(grp);
132
133 if (id > 0)
134 grp->gr_gid = (gid_t) id;
135
136 if (a_newname != NULL)
137 grp->gr_name = pw_checkname(a_newname->val, 0);
138 } else {
139 if (name == NULL) /* Required */
140 errx(EX_DATAERR, "group name required");
141 else if (grp != NULL) /* Exists */
142 errx(EX_DATAERR, "group name `%s' already exists", name);
143
144 extendarray(&members, &grmembers, 200);
145 members[0] = NULL;
146 grp = &fakegroup;
147 grp->gr_name = pw_checkname(name, 0);
148 grp->gr_passwd = "*";
149 grp->gr_gid = gr_gidpolicy(cnf, args);
150 grp->gr_mem = members;
151 }
152
153 /*
154 * This allows us to set a group password Group passwords is an
155 * antique idea, rarely used and insecure (no secure database) Should
156 * be discouraged, but it is apparently still supported by some
157 * software.
158 */
159
160 if ((arg = getarg(args, 'h')) != NULL ||
161 (arg = getarg(args, 'H')) != NULL) {
162 if (strcmp(arg->val, "-") == 0)
163 grp->gr_passwd = "*"; /* No access */
164 else {
165 int fd = atoi(arg->val);
166 int precrypt = (arg->ch == 'H');
167 int b;
168 int istty = isatty(fd);
169 struct termios t;
170 char *p, line[256];
171
172 if (istty) {
173 if (tcgetattr(fd, &t) == -1)
174 istty = 0;
175 else {
176 struct termios n = t;
177
178 /* Disable echo */
179 n.c_lflag &= ~(ECHO);
180 tcsetattr(fd, TCSANOW, &n);
181 printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
182 fflush(stdout);
183 }
184 }
185 b = read(fd, line, sizeof(line) - 1);
186 if (istty) { /* Restore state */
187 tcsetattr(fd, TCSANOW, &t);
188 fputc('\n', stdout);
189 fflush(stdout);
190 }
191 if (b < 0)
192 err(EX_OSERR, "-h file descriptor");
193 line[b] = '\0';
194 if ((p = strpbrk(line, " \t\r\n")) != NULL)
195 *p = '\0';
196 if (!*line)
197 errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
198 if (precrypt) {
199 if (strchr(line, ':') != NULL)
200 return EX_DATAERR;
201 grp->gr_passwd = line;
202 } else
203 grp->gr_passwd = pw_pwcrypt(line);
204 }
205 }
206
207 if (((arg = getarg(args, 'M')) != NULL ||
208 (arg = getarg(args, 'd')) != NULL ||
209 (arg = getarg(args, 'm')) != NULL) && arg->val) {
210 int i = 0;
211 char *p;
212 struct passwd *pwd;
213
214 /* Make sure this is not stay NULL with -M "" */
215 extendarray(&members, &grmembers, 200);
216 if (arg->ch == 'd')
217 delete_members(&members, &grmembers, &i, arg, grp);
218 else if (arg->ch == 'm') {
219 int k = 0;
220
221 if (grp->gr_mem != NULL) {
222 while (grp->gr_mem[k] != NULL) {
223 if (extendarray(&members, &grmembers, i + 2) != -1)
224 members[i++] = grp->gr_mem[k];
225 k++;
226 }
227 }
228 }
229
230 if (arg->ch != 'd')
231 for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
232 int j;
233
234 /*
235 * Check for duplicates
236 */
237 pwd = lookup_pwent(p);
238 for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++)
239 ;
240 if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
241 members[i++] = newstr(pwd->pw_name);
242 }
243 while (i < grmembers)
244 members[i++] = NULL;
245 grp->gr_mem = members;
246 }
247
248 if (conf.dryrun)
249 return print_group(grp);
250
251 if (mode == M_ADD && (rc = addgrent(grp)) != 0) {
252 if (rc == -1)
253 errx(EX_IOERR, "group '%s' already exists",
254 grp->gr_name);
255 else
256 err(EX_IOERR, "group update");
257 } else if (mode == M_UPDATE && (rc = chggrent(name, grp)) != 0) {
258 if (rc == -1)
259 errx(EX_IOERR, "group '%s' not available (NIS?)",
260 grp->gr_name);
261 else
262 err(EX_IOERR, "group update");
263 }
264
265 if (a_newname != NULL)
266 name = a_newname->val;
267 /* grp may have been invalidated */
268 if ((grp = GETGRNAM(name)) == NULL)
269 errx(EX_SOFTWARE, "group disappeared during update");
270
271 pw_log(cnf, mode, W_GROUP, "%s(%u)", grp->gr_name, grp->gr_gid);
272
273 free(members);
274
275 return EXIT_SUCCESS;
276 }
277
278
279 /*
280 * Lookup a passwd entry using a name or UID.
281 */
282 static struct passwd *
283 lookup_pwent(const char *user)
284 {
285 struct passwd *pwd;
286
287 if ((pwd = GETPWNAM(user)) == NULL &&
288 (!isdigit((unsigned char)*user) ||
289 (pwd = getpwuid((uid_t) atoi(user))) == NULL))
290 errx(EX_NOUSER, "user `%s' does not exist", user);
291
292 return (pwd);
293 }
294
295
296 /*
297 * Delete requested members from a group.
298 */
299 static void
300 delete_members(char ***members, int *grmembers, int *i, struct carg *arg,
301 struct group *grp)
302 {
303 bool matchFound;
304 char *user;
305 char *valueCopy;
306 char *valuePtr;
307 int k;
308 struct passwd *pwd;
309
310 if (grp->gr_mem == NULL)
311 return;
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 `%u' has already been allocated", 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)
404 {
405 if (!conf.pretty) {
406 char *buf = NULL;
407
408 buf = gr_make(grp);
409 printf("%s\n", buf);
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 if (grp->gr_mem != NULL) {
418 for (i = 0; grp->gr_mem[i]; i++)
419 printf("%s%s", i ? "," : "", grp->gr_mem[i]);
420 }
421 fputs("\n\n", stdout);
422 }
423 return EXIT_SUCCESS;
424 }