gr_util.c revision 244736
1178431Sscf/*-
2178431Sscf * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org>
3178431Sscf * All rights reserved.
4178431Sscf *
5178431Sscf * Redistribution and use in source and binary forms, with or without
6178431Sscf * modification, are permitted provided that the following conditions
7178431Sscf * are met:
8178431Sscf * 1. Redistributions of source code must retain the above copyright
9178431Sscf *    notice, this list of conditions and the following disclaimer,
10178431Sscf *    without modification, immediately at the beginning of the file.
11178431Sscf * 2. Redistributions in binary form must reproduce the above copyright
12178431Sscf *    notice, this list of conditions and the following disclaimer in the
13178431Sscf *    documentation and/or other materials provided with the distribution.
14178431Sscf *
15178431Sscf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16178431Sscf * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17178431Sscf * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18178431Sscf * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19178431Sscf * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20178431Sscf * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21178431Sscf * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22178431Sscf * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23178431Sscf * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24178431Sscf * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25178431Sscf */
26178431Sscf
27178431Sscf#include <sys/cdefs.h>
28178431Sscf__FBSDID("$FreeBSD: head/lib/libutil/gr_util.c 244736 2012-12-27 14:30:19Z bapt $");
29178431Sscf
30178431Sscf#include <sys/param.h>
31228545Sbapt#include <sys/errno.h>
32228545Sbapt#include <sys/stat.h>
33184831Sscf
34228545Sbapt#include <ctype.h>
35228545Sbapt#include <err.h>
36228545Sbapt#include <fcntl.h>
37178431Sscf#include <grp.h>
38178431Sscf#include <inttypes.h>
39184831Sscf#include <libutil.h>
40228545Sbapt#include <paths.h>
41178431Sscf#include <stdbool.h>
42178431Sscf#include <stdio.h>
43178431Sscf#include <stdlib.h>
44178431Sscf#include <string.h>
45228545Sbapt#include <unistd.h>
46178431Sscf
47185237Sscfstruct group_storage {
48185237Sscf	struct group	 gr;
49185237Sscf	char		*members[];
50185237Sscf};
51178431Sscf
52228545Sbaptstatic int lockfd = -1;
53228545Sbaptstatic char group_dir[PATH_MAX];
54228545Sbaptstatic char group_file[PATH_MAX];
55228545Sbaptstatic char tempname[PATH_MAX];
56228545Sbaptstatic int initialized;
57228545Sbapt
58185237Sscfstatic const char group_line_format[] = "%s:%s:%ju:";
59185237Sscf
60178431Sscf/*
61228545Sbapt * Initialize statics
62228545Sbapt */
63228545Sbaptint
64228545Sbaptgr_init(const char *dir, const char *group)
65228545Sbapt{
66242319Sbapt
67228545Sbapt	if (dir == NULL) {
68228545Sbapt		strcpy(group_dir, _PATH_ETC);
69228545Sbapt	} else {
70228545Sbapt		if (strlen(dir) >= sizeof(group_dir)) {
71228545Sbapt			errno = ENAMETOOLONG;
72228545Sbapt			return (-1);
73228545Sbapt		}
74228545Sbapt		strcpy(group_dir, dir);
75228545Sbapt	}
76228545Sbapt
77228545Sbapt	if (group == NULL) {
78228545Sbapt		if (dir == NULL) {
79228545Sbapt			strcpy(group_file, _PATH_GROUP);
80228545Sbapt		} else if (snprintf(group_file, sizeof(group_file), "%s/group",
81228545Sbapt			group_dir) > (int)sizeof(group_file)) {
82228545Sbapt			errno = ENAMETOOLONG;
83228545Sbapt			return (-1);
84228545Sbapt		}
85228545Sbapt	} else {
86228545Sbapt		if (strlen(group) >= sizeof(group_file)) {
87228545Sbapt			errno = ENAMETOOLONG;
88228545Sbapt			return (-1);
89228545Sbapt		}
90228545Sbapt		strcpy(group_file, group);
91228545Sbapt	}
92242319Sbapt
93228545Sbapt	initialized = 1;
94228545Sbapt	return (0);
95228545Sbapt}
96228545Sbapt
97228545Sbapt/*
98228545Sbapt * Lock the group file
99228545Sbapt */
100228545Sbaptint
101228545Sbaptgr_lock(void)
102228545Sbapt{
103228545Sbapt	if (*group_file == '\0')
104228545Sbapt		return (-1);
105228545Sbapt
106228545Sbapt	for (;;) {
107228545Sbapt		struct stat st;
108228545Sbapt
109244735Sbapt		lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK, 0);
110244735Sbapt		if (lockfd == -1) {
111228545Sbapt			if (errno == EWOULDBLOCK) {
112228545Sbapt				errx(1, "the group file is busy");
113228545Sbapt			} else {
114228545Sbapt				err(1, "could not lock the group file: ");
115228545Sbapt			}
116228545Sbapt		}
117228545Sbapt		if (fstat(lockfd, &st) == -1)
118228545Sbapt			err(1, "fstat() failed: ");
119228545Sbapt		if (st.st_nlink != 0)
120228545Sbapt			break;
121228545Sbapt		close(lockfd);
122228545Sbapt		lockfd = -1;
123228545Sbapt	}
124228545Sbapt	return (lockfd);
125228545Sbapt}
126228545Sbapt
127228545Sbapt/*
128228545Sbapt * Create and open a presmuably safe temp file for editing group data
129228545Sbapt */
130228545Sbaptint
131228545Sbaptgr_tmp(int mfd)
132228545Sbapt{
133228545Sbapt	char buf[8192];
134228545Sbapt	ssize_t nr;
135228545Sbapt	const char *p;
136228545Sbapt	int tfd;
137228545Sbapt
138228545Sbapt	if (*group_file == '\0')
139228545Sbapt		return (-1);
140228545Sbapt	if ((p = strrchr(group_file, '/')))
141228545Sbapt		++p;
142228545Sbapt	else
143228545Sbapt		p = group_file;
144228545Sbapt	if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX",
145228545Sbapt		(int)(p - group_file), group_file) >= (int)sizeof(tempname)) {
146228545Sbapt		errno = ENAMETOOLONG;
147228545Sbapt		return (-1);
148228545Sbapt	}
149228545Sbapt	if ((tfd = mkstemp(tempname)) == -1)
150228545Sbapt		return (-1);
151228545Sbapt	if (mfd != -1) {
152228545Sbapt		while ((nr = read(mfd, buf, sizeof(buf))) > 0)
153228545Sbapt			if (write(tfd, buf, (size_t)nr) != nr)
154228545Sbapt				break;
155228545Sbapt		if (nr != 0) {
156228545Sbapt			unlink(tempname);
157228545Sbapt			*tempname = '\0';
158228545Sbapt			close(tfd);
159228545Sbapt			return (-1);
160228545Sbapt		}
161228545Sbapt	}
162228545Sbapt	return (tfd);
163228545Sbapt}
164228545Sbapt
165228545Sbapt/*
166228545Sbapt * Copy the group file from one descriptor to another, replacing, deleting
167228545Sbapt * or adding a single record on the way.
168228545Sbapt */
169228545Sbaptint
170228545Sbaptgr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr)
171228545Sbapt{
172228545Sbapt	char buf[8192], *end, *line, *p, *q, *r, t;
173228545Sbapt	struct group *fgr;
174228545Sbapt	const struct group *sgr;
175228545Sbapt	size_t len;
176228545Sbapt	int eof, readlen;
177228545Sbapt
178228545Sbapt	sgr = gr;
179228545Sbapt	if (gr == NULL) {
180228545Sbapt		line = NULL;
181228545Sbapt		if (old_gr == NULL)
182228545Sbapt			return (-1);
183228545Sbapt		sgr = old_gr;
184228545Sbapt	} else if ((line = gr_make(gr)) == NULL)
185228545Sbapt		return (-1);
186228545Sbapt
187228545Sbapt	eof = 0;
188228545Sbapt	len = 0;
189228545Sbapt	p = q = end = buf;
190228545Sbapt	for (;;) {
191228545Sbapt		/* find the end of the current line */
192228545Sbapt		for (p = q; q < end && *q != '\0'; ++q)
193228545Sbapt			if (*q == '\n')
194228545Sbapt				break;
195228545Sbapt
196228545Sbapt		/* if we don't have a complete line, fill up the buffer */
197228545Sbapt		if (q >= end) {
198228545Sbapt			if (eof)
199228545Sbapt				break;
200228545Sbapt			if ((size_t)(q - p) >= sizeof(buf)) {
201228545Sbapt				warnx("group line too long");
202228545Sbapt				errno = EINVAL; /* hack */
203228545Sbapt				goto err;
204228545Sbapt			}
205228545Sbapt			if (p < end) {
206228545Sbapt				q = memmove(buf, p, end -p);
207228545Sbapt				end -= p - buf;
208228545Sbapt			} else {
209228545Sbapt				p = q = end = buf;
210228545Sbapt			}
211228545Sbapt			readlen = read(ffd, end, sizeof(buf) - (end -buf));
212228545Sbapt			if (readlen == -1)
213228545Sbapt				goto err;
214228545Sbapt			else
215228545Sbapt				len = (size_t)readlen;
216228545Sbapt			if (len == 0 && p == buf)
217228545Sbapt				break;
218228545Sbapt			end += len;
219228545Sbapt			len = end - buf;
220228545Sbapt			if (len < (ssize_t)sizeof(buf)) {
221228545Sbapt				eof = 1;
222228545Sbapt				if (len > 0 && buf[len -1] != '\n')
223228545Sbapt					++len, *end++ = '\n';
224228545Sbapt			}
225228545Sbapt			continue;
226228545Sbapt		}
227228545Sbapt
228228545Sbapt		/* is it a blank line or a comment? */
229228545Sbapt		for (r = p; r < q && isspace(*r); ++r)
230228545Sbapt			/* nothing */;
231228545Sbapt		if (r == q || *r == '#') {
232228545Sbapt			/* yep */
233228545Sbapt			if (write(tfd, p, q -p + 1) != q - p + 1)
234228545Sbapt				goto err;
235228545Sbapt			++q;
236228545Sbapt			continue;
237228545Sbapt		}
238228545Sbapt
239228545Sbapt		/* is it the one we're looking for? */
240228545Sbapt
241228545Sbapt		t = *q;
242228545Sbapt		*q = '\0';
243228545Sbapt
244228545Sbapt		fgr = gr_scan(r);
245228545Sbapt
246228545Sbapt		/* fgr is either a struct group for the current line,
247228545Sbapt		 * or NULL if the line is malformed.
248228545Sbapt		 */
249228545Sbapt
250228545Sbapt		*q = t;
251228545Sbapt		if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) {
252228545Sbapt			/* nope */
253228545Sbapt			if (fgr != NULL)
254228545Sbapt				free(fgr);
255228545Sbapt			if (write(tfd, p, q - p + 1) != q - p + 1)
256228545Sbapt				goto err;
257228545Sbapt			++q;
258228545Sbapt			continue;
259228545Sbapt		}
260228545Sbapt		if (old_gr && !gr_equal(fgr, old_gr)) {
261228545Sbapt			warnx("entry inconsistent");
262228545Sbapt			free(fgr);
263228545Sbapt			errno = EINVAL; /* hack */
264228545Sbapt			goto err;
265228545Sbapt		}
266228545Sbapt		free(fgr);
267228545Sbapt
268228545Sbapt		/* it is, replace or remove it */
269228545Sbapt		if (line != NULL) {
270228545Sbapt			len = strlen(line);
271228545Sbapt			if (write(tfd, line, len) != (int) len)
272228545Sbapt				goto err;
273228545Sbapt		} else {
274228545Sbapt			/* when removed, avoid the \n */
275228545Sbapt			q++;
276228545Sbapt		}
277228545Sbapt		/* we're done, just copy the rest over */
278228545Sbapt		for (;;) {
279228545Sbapt			if (write(tfd, q, end - q) != end - q)
280228545Sbapt				goto err;
281228545Sbapt			q = buf;
282228545Sbapt			readlen = read(ffd, buf, sizeof(buf));
283228545Sbapt			if (readlen == 0)
284228545Sbapt				break;
285228545Sbapt			else
286228545Sbapt				len = (size_t)readlen;
287228545Sbapt			if (readlen == -1)
288228545Sbapt				goto err;
289228545Sbapt			end = buf + len;
290228545Sbapt		}
291228545Sbapt		goto done;
292228545Sbapt	}
293228545Sbapt
294228545Sbapt	/* if we got here, we didn't find the old entry */
295228545Sbapt	if (line == NULL) {
296228545Sbapt		errno = ENOENT;
297228545Sbapt		goto err;
298228545Sbapt	}
299228545Sbapt	len = strlen(line);
300228545Sbapt	if ((size_t)write(tfd, line, len) != len ||
301228545Sbapt	   write(tfd, "\n", 1) != 1)
302228545Sbapt		goto err;
303228545Sbapt done:
304228545Sbapt	if (line != NULL)
305228545Sbapt		free(line);
306228545Sbapt	return (0);
307228545Sbapt err:
308228545Sbapt	if (line != NULL)
309228545Sbapt		free(line);
310228545Sbapt	return (-1);
311228545Sbapt}
312228545Sbapt
313228545Sbapt/*
314228545Sbapt * Regenerate the group file
315228545Sbapt */
316228545Sbaptint
317228545Sbaptgr_mkdb(void)
318228545Sbapt{
319243334Sbapt	if (chmod(tempname, 0644) != 0)
320243334Sbapt		return (-1);
321243328Sbapt
322243334Sbapt	return (rename(tempname, group_file));
323228545Sbapt}
324228545Sbapt
325228545Sbapt/*
326228545Sbapt * Clean up. Preserver errno for the caller's convenience.
327228545Sbapt */
328228545Sbaptvoid
329228545Sbaptgr_fini(void)
330228545Sbapt{
331228545Sbapt	int serrno;
332228545Sbapt
333228545Sbapt	if (!initialized)
334228545Sbapt		return;
335228545Sbapt	initialized = 0;
336228545Sbapt	serrno = errno;
337228545Sbapt	if (*tempname != '\0') {
338228545Sbapt		unlink(tempname);
339228545Sbapt		*tempname = '\0';
340228545Sbapt	}
341228545Sbapt	if (lockfd != -1)
342228545Sbapt		close(lockfd);
343228545Sbapt	errno = serrno;
344228545Sbapt}
345228545Sbapt
346228545Sbapt/*
347178431Sscf * Compares two struct group's.
348178431Sscf */
349178431Sscfint
350178431Sscfgr_equal(const struct group *gr1, const struct group *gr2)
351178431Sscf{
352185237Sscf	int gr1_ndx;
353185237Sscf	int gr2_ndx;
354184831Sscf	bool found;
355178431Sscf
356178431Sscf	/* Check that the non-member information is the same. */
357185237Sscf	if (gr1->gr_name == NULL || gr2->gr_name == NULL) {
358185237Sscf		if (gr1->gr_name != gr2->gr_name)
359185237Sscf			return (false);
360185237Sscf	} else if (strcmp(gr1->gr_name, gr2->gr_name) != 0)
361185237Sscf		return (false);
362185237Sscf	if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) {
363185237Sscf		if (gr1->gr_passwd != gr2->gr_passwd)
364185237Sscf			return (false);
365185237Sscf	} else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0)
366185237Sscf		return (false);
367185237Sscf	if (gr1->gr_gid != gr2->gr_gid)
368185237Sscf		return (false);
369178431Sscf
370178431Sscf	/* Check all members in both groups. */
371185237Sscf	if (gr1->gr_mem == NULL || gr2->gr_mem == NULL) {
372185237Sscf		if (gr1->gr_mem != gr2->gr_mem)
373185237Sscf			return (false);
374185237Sscf	} else {
375185237Sscf		for (found = false, gr1_ndx = 0; gr1->gr_mem[gr1_ndx] != NULL;
376185237Sscf		    gr1_ndx++) {
377185237Sscf			for (gr2_ndx = 0; gr2->gr_mem[gr2_ndx] != NULL;
378185237Sscf			    gr2_ndx++)
379185237Sscf				if (strcmp(gr1->gr_mem[gr1_ndx],
380185237Sscf				    gr2->gr_mem[gr2_ndx]) == 0) {
381178431Sscf					found = true;
382178431Sscf					break;
383178431Sscf				}
384185237Sscf			if (!found)
385185237Sscf				return (false);
386178431Sscf		}
387178431Sscf
388178431Sscf		/* Check that group2 does not have more members than group1. */
389185237Sscf		if (gr2->gr_mem[gr1_ndx] != NULL)
390185237Sscf			return (false);
391178431Sscf	}
392178431Sscf
393185237Sscf	return (true);
394178431Sscf}
395178431Sscf
396178431Sscf/*
397178431Sscf * Make a group line out of a struct group.
398178431Sscf */
399178431Sscfchar *
400178431Sscfgr_make(const struct group *gr)
401178431Sscf{
402178431Sscf	char *line;
403185237Sscf	size_t line_size;
404178431Sscf	int ndx;
405178431Sscf
406178431Sscf	/* Calculate the length of the group line. */
407185237Sscf	line_size = snprintf(NULL, 0, group_line_format, gr->gr_name,
408178431Sscf	    gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1;
409185237Sscf	if (gr->gr_mem != NULL) {
410185237Sscf		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++)
411185237Sscf			line_size += strlen(gr->gr_mem[ndx]) + 1;
412185237Sscf		if (ndx > 0)
413185237Sscf			line_size--;
414185237Sscf	}
415178431Sscf
416178431Sscf	/* Create the group line and fill it. */
417185237Sscf	if ((line = malloc(line_size)) == NULL)
418178431Sscf		return (NULL);
419200423Sscf	snprintf(line, line_size, group_line_format, gr->gr_name, gr->gr_passwd,
420200423Sscf	    (uintmax_t)gr->gr_gid);
421185237Sscf	if (gr->gr_mem != NULL)
422185237Sscf		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) {
423185237Sscf			strcat(line, gr->gr_mem[ndx]);
424185237Sscf			if (gr->gr_mem[ndx + 1] != NULL)
425185237Sscf				strcat(line, ",");
426185237Sscf		}
427178431Sscf
428178431Sscf	return (line);
429178431Sscf}
430178431Sscf
431178431Sscf/*
432178431Sscf * Duplicate a struct group.
433178431Sscf */
434178431Sscfstruct group *
435178431Sscfgr_dup(const struct group *gr)
436178431Sscf{
437185237Sscf	char *dst;
438184831Sscf	size_t len;
439185237Sscf	struct group_storage *gs;
440178431Sscf	int ndx;
441185237Sscf	int num_mem;
442178431Sscf
443185237Sscf	/* Calculate size of the group. */
444185237Sscf	len = sizeof(*gs);
445185237Sscf	if (gr->gr_name != NULL)
446185237Sscf		len += strlen(gr->gr_name) + 1;
447185237Sscf	if (gr->gr_passwd != NULL)
448185237Sscf		len += strlen(gr->gr_passwd) + 1;
449178431Sscf	if (gr->gr_mem != NULL) {
450185237Sscf		for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++)
451185237Sscf			len += strlen(gr->gr_mem[num_mem]) + 1;
452185237Sscf		len += (num_mem + 1) * sizeof(*gr->gr_mem);
453185237Sscf	} else
454185237Sscf		num_mem = -1;
455178431Sscf
456178431Sscf	/* Create new group and copy old group into it. */
457185237Sscf	if ((gs = calloc(1, len)) == NULL)
458178431Sscf		return (NULL);
459185237Sscf	dst = (char *)&gs->members[num_mem + 1];
460178431Sscf	if (gr->gr_name != NULL) {
461185237Sscf		gs->gr.gr_name = dst;
462185237Sscf		dst = stpcpy(gs->gr.gr_name, gr->gr_name) + 1;
463178431Sscf	}
464178431Sscf	if (gr->gr_passwd != NULL) {
465185237Sscf		gs->gr.gr_passwd = dst;
466185237Sscf		dst = stpcpy(gs->gr.gr_passwd, gr->gr_passwd) + 1;
467178431Sscf	}
468185237Sscf	gs->gr.gr_gid = gr->gr_gid;
469178431Sscf	if (gr->gr_mem != NULL) {
470185237Sscf		gs->gr.gr_mem = gs->members;
471185237Sscf		for (ndx = 0; ndx < num_mem; ndx++) {
472185237Sscf			gs->gr.gr_mem[ndx] = dst;
473185237Sscf			dst = stpcpy(gs->gr.gr_mem[ndx], gr->gr_mem[ndx]) + 1;
474178431Sscf		}
475185237Sscf		gs->gr.gr_mem[ndx] = NULL;
476178431Sscf	}
477178431Sscf
478185237Sscf	return (&gs->gr);
479178431Sscf}
480178431Sscf
481178431Sscf/*
482244736Sbapt * Add a new member name to a struct group.
483244736Sbapt */
484244736Sbaptstruct group *
485244736Sbaptgr_add(struct group *gr, const char *newmember)
486244736Sbapt{
487244736Sbapt	size_t mlen;
488244736Sbapt	int num_mem=0;
489244736Sbapt	char **members;
490244736Sbapt	struct group *newgr;
491244736Sbapt
492244736Sbapt	if (newmember == NULL)
493244736Sbapt		return(gr_dup(gr));
494244736Sbapt
495244736Sbapt	if (gr->gr_mem != NULL) {
496244736Sbapt		for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++) {
497244736Sbapt			if (strcmp(gr->gr_mem[num_mem], newmember) == 0) {
498244736Sbapt				errno = EEXIST;
499244736Sbapt				return (NULL);
500244736Sbapt			}
501244736Sbapt		}
502244736Sbapt	}
503244736Sbapt	/* Allocate enough for current pointers + 1 more and NULL marker */
504244736Sbapt	mlen = (num_mem + 2) * sizeof(*gr->gr_mem);
505244736Sbapt	if ((members = calloc(1, mlen )) == NULL) {
506244736Sbapt		errno = ENOMEM;
507244736Sbapt		return (NULL);
508244736Sbapt	}
509244736Sbapt	memcpy(members, gr->gr_mem, num_mem * sizeof(*gr->gr_mem));
510244736Sbapt	members[num_mem++] = (char *)newmember;
511244736Sbapt	members[num_mem] = NULL;
512244736Sbapt	gr->gr_mem = members;
513244736Sbapt	newgr = gr_dup(gr);
514244736Sbapt	if (newgr == NULL)
515244736Sbapt		errno = ENOMEM;
516244736Sbapt
517244736Sbapt	free(members);
518244736Sbapt	return (newgr);
519244736Sbapt}
520244736Sbapt
521244736Sbapt/*
522178431Sscf * Scan a line and place it into a group structure.
523178431Sscf */
524178431Sscfstatic bool
525178431Sscf__gr_scan(char *line, struct group *gr)
526178431Sscf{
527178431Sscf	char *loc;
528178431Sscf	int ndx;
529178431Sscf
530178431Sscf	/* Assign non-member information to structure. */
531178431Sscf	gr->gr_name = line;
532178431Sscf	if ((loc = strchr(line, ':')) == NULL)
533178431Sscf		return (false);
534178431Sscf	*loc = '\0';
535178431Sscf	gr->gr_passwd = loc + 1;
536184831Sscf	if (*gr->gr_passwd == ':')
537184831Sscf		*gr->gr_passwd = '\0';
538178431Sscf	else {
539178431Sscf		if ((loc = strchr(loc + 1, ':')) == NULL)
540178431Sscf			return (false);
541178431Sscf		*loc = '\0';
542178431Sscf	}
543184831Sscf	if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1)
544178431Sscf		return (false);
545178431Sscf
546178431Sscf	/* Assign member information to structure. */
547178431Sscf	if ((loc = strchr(loc + 1, ':')) == NULL)
548178431Sscf		return (false);
549178431Sscf	line = loc + 1;
550178431Sscf	gr->gr_mem = NULL;
551185237Sscf	ndx = 0;
552185237Sscf	do {
553185237Sscf		gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) *
554185237Sscf		    (ndx + 1));
555185237Sscf		if (gr->gr_mem == NULL)
556185237Sscf			return (false);
557185237Sscf
558185237Sscf		/* Skip locations without members (i.e., empty string). */
559178431Sscf		do {
560178431Sscf			gr->gr_mem[ndx] = strsep(&line, ",");
561185237Sscf		} while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0');
562185237Sscf	} while (gr->gr_mem[ndx++] != NULL);
563178431Sscf
564178431Sscf	return (true);
565178431Sscf}
566178431Sscf
567178431Sscf/*
568178431Sscf * Create a struct group from a line.
569178431Sscf */
570178431Sscfstruct group *
571178431Sscfgr_scan(const char *line)
572178431Sscf{
573184831Sscf	struct group gr;
574185237Sscf	char *line_copy;
575185237Sscf	struct group *new_gr;
576178431Sscf
577185237Sscf	if ((line_copy = strdup(line)) == NULL)
578178431Sscf		return (NULL);
579185237Sscf	if (!__gr_scan(line_copy, &gr)) {
580185237Sscf		free(line_copy);
581178431Sscf		return (NULL);
582178431Sscf	}
583185237Sscf	new_gr = gr_dup(&gr);
584185237Sscf	free(line_copy);
585178431Sscf	if (gr.gr_mem != NULL)
586178431Sscf		free(gr.gr_mem);
587178431Sscf
588185237Sscf	return (new_gr);
589178431Sscf}
590