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