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: stable/10/lib/libutil/gr_util.c 310480 2016-12-23 15:05:41Z des $");
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
47228545Sbaptstatic int lockfd = -1;
48228545Sbaptstatic char group_dir[PATH_MAX];
49228545Sbaptstatic char group_file[PATH_MAX];
50228545Sbaptstatic char tempname[PATH_MAX];
51228545Sbaptstatic int initialized;
52247919Sdbstatic size_t grmemlen(const struct group *, const char *, int *);
53248102Sdbstatic struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx);
54228545Sbapt
55178431Sscf/*
56228545Sbapt * Initialize statics
57228545Sbapt */
58228545Sbaptint
59228545Sbaptgr_init(const char *dir, const char *group)
60228545Sbapt{
61242319Sbapt
62228545Sbapt	if (dir == NULL) {
63228545Sbapt		strcpy(group_dir, _PATH_ETC);
64228545Sbapt	} else {
65228545Sbapt		if (strlen(dir) >= sizeof(group_dir)) {
66228545Sbapt			errno = ENAMETOOLONG;
67228545Sbapt			return (-1);
68228545Sbapt		}
69228545Sbapt		strcpy(group_dir, dir);
70228545Sbapt	}
71228545Sbapt
72228545Sbapt	if (group == NULL) {
73228545Sbapt		if (dir == NULL) {
74228545Sbapt			strcpy(group_file, _PATH_GROUP);
75228545Sbapt		} else if (snprintf(group_file, sizeof(group_file), "%s/group",
76228545Sbapt			group_dir) > (int)sizeof(group_file)) {
77228545Sbapt			errno = ENAMETOOLONG;
78228545Sbapt			return (-1);
79228545Sbapt		}
80228545Sbapt	} else {
81228545Sbapt		if (strlen(group) >= sizeof(group_file)) {
82228545Sbapt			errno = ENAMETOOLONG;
83228545Sbapt			return (-1);
84228545Sbapt		}
85228545Sbapt		strcpy(group_file, group);
86228545Sbapt	}
87242319Sbapt
88228545Sbapt	initialized = 1;
89228545Sbapt	return (0);
90228545Sbapt}
91228545Sbapt
92228545Sbapt/*
93228545Sbapt * Lock the group file
94228545Sbapt */
95228545Sbaptint
96228545Sbaptgr_lock(void)
97228545Sbapt{
98228545Sbapt	if (*group_file == '\0')
99228545Sbapt		return (-1);
100228545Sbapt
101228545Sbapt	for (;;) {
102228545Sbapt		struct stat st;
103228545Sbapt
104244744Sbapt		lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0);
105244735Sbapt		if (lockfd == -1) {
106228545Sbapt			if (errno == EWOULDBLOCK) {
107228545Sbapt				errx(1, "the group file is busy");
108228545Sbapt			} else {
109228545Sbapt				err(1, "could not lock the group file: ");
110228545Sbapt			}
111228545Sbapt		}
112228545Sbapt		if (fstat(lockfd, &st) == -1)
113228545Sbapt			err(1, "fstat() failed: ");
114228545Sbapt		if (st.st_nlink != 0)
115228545Sbapt			break;
116228545Sbapt		close(lockfd);
117228545Sbapt		lockfd = -1;
118228545Sbapt	}
119228545Sbapt	return (lockfd);
120228545Sbapt}
121228545Sbapt
122228545Sbapt/*
123228545Sbapt * Create and open a presmuably safe temp file for editing group data
124228545Sbapt */
125228545Sbaptint
126228545Sbaptgr_tmp(int mfd)
127228545Sbapt{
128228545Sbapt	char buf[8192];
129228545Sbapt	ssize_t nr;
130228545Sbapt	const char *p;
131228545Sbapt	int tfd;
132228545Sbapt
133228545Sbapt	if (*group_file == '\0')
134228545Sbapt		return (-1);
135228545Sbapt	if ((p = strrchr(group_file, '/')))
136228545Sbapt		++p;
137228545Sbapt	else
138228545Sbapt		p = group_file;
139228545Sbapt	if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX",
140228545Sbapt		(int)(p - group_file), group_file) >= (int)sizeof(tempname)) {
141228545Sbapt		errno = ENAMETOOLONG;
142228545Sbapt		return (-1);
143228545Sbapt	}
144310173Sasomers	if ((tfd = mkostemp(tempname, 0)) == -1)
145228545Sbapt		return (-1);
146228545Sbapt	if (mfd != -1) {
147228545Sbapt		while ((nr = read(mfd, buf, sizeof(buf))) > 0)
148228545Sbapt			if (write(tfd, buf, (size_t)nr) != nr)
149228545Sbapt				break;
150228545Sbapt		if (nr != 0) {
151228545Sbapt			unlink(tempname);
152228545Sbapt			*tempname = '\0';
153228545Sbapt			close(tfd);
154228545Sbapt			return (-1);
155228545Sbapt		}
156228545Sbapt	}
157228545Sbapt	return (tfd);
158228545Sbapt}
159228545Sbapt
160228545Sbapt/*
161228545Sbapt * Copy the group file from one descriptor to another, replacing, deleting
162228545Sbapt * or adding a single record on the way.
163228545Sbapt */
164228545Sbaptint
165228545Sbaptgr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr)
166228545Sbapt{
167310480Sdes	char *buf, *end, *line, *p, *q, *r, *tmp;
168228545Sbapt	struct group *fgr;
169228545Sbapt	const struct group *sgr;
170310480Sdes	size_t len, size;
171228545Sbapt	int eof, readlen;
172310480Sdes	char t;
173228545Sbapt
174274082Sbapt	if (old_gr == NULL && gr == NULL)
175274082Sbapt		return(-1);
176274082Sbapt
177274082Sbapt	sgr = old_gr;
178274082Sbapt	/* deleting a group */
179228545Sbapt	if (gr == NULL) {
180228545Sbapt		line = NULL;
181274082Sbapt	} else {
182274082Sbapt		if ((line = gr_make(gr)) == NULL)
183228545Sbapt			return (-1);
184274082Sbapt	}
185228545Sbapt
186274082Sbapt	/* adding a group */
187274082Sbapt	if (sgr == NULL)
188274082Sbapt		sgr = gr;
189274082Sbapt
190310480Sdes	/* initialize the buffer */
191310480Sdes	if ((buf = malloc(size = 1024)) == NULL)
192310480Sdes		goto err;
193310480Sdes
194228545Sbapt	eof = 0;
195228545Sbapt	len = 0;
196228545Sbapt	p = q = end = buf;
197228545Sbapt	for (;;) {
198228545Sbapt		/* find the end of the current line */
199228545Sbapt		for (p = q; q < end && *q != '\0'; ++q)
200228545Sbapt			if (*q == '\n')
201228545Sbapt				break;
202228545Sbapt
203228545Sbapt		/* if we don't have a complete line, fill up the buffer */
204228545Sbapt		if (q >= end) {
205228545Sbapt			if (eof)
206228545Sbapt				break;
207310480Sdes			while ((size_t)(q - p) >= size) {
208310480Sdes				if ((tmp = realloc(buf, size * 2)) == NULL) {
209310480Sdes					warnx("group line too long");
210310480Sdes					goto err;
211310480Sdes				}
212310480Sdes				p = tmp + (p - buf);
213310480Sdes				q = tmp + (q - buf);
214310480Sdes				end = tmp + (end - buf);
215310480Sdes				buf = tmp;
216310480Sdes				size = size * 2;
217228545Sbapt			}
218228545Sbapt			if (p < end) {
219228545Sbapt				q = memmove(buf, p, end -p);
220228545Sbapt				end -= p - buf;
221228545Sbapt			} else {
222228545Sbapt				p = q = end = buf;
223228545Sbapt			}
224310480Sdes			readlen = read(ffd, end, size - (end - buf));
225228545Sbapt			if (readlen == -1)
226228545Sbapt				goto err;
227228545Sbapt			else
228228545Sbapt				len = (size_t)readlen;
229228545Sbapt			if (len == 0 && p == buf)
230228545Sbapt				break;
231228545Sbapt			end += len;
232228545Sbapt			len = end - buf;
233310480Sdes			if (len < size) {
234228545Sbapt				eof = 1;
235228545Sbapt				if (len > 0 && buf[len -1] != '\n')
236228545Sbapt					++len, *end++ = '\n';
237228545Sbapt			}
238228545Sbapt			continue;
239228545Sbapt		}
240228545Sbapt
241228545Sbapt		/* is it a blank line or a comment? */
242228545Sbapt		for (r = p; r < q && isspace(*r); ++r)
243228545Sbapt			/* nothing */;
244228545Sbapt		if (r == q || *r == '#') {
245228545Sbapt			/* yep */
246228545Sbapt			if (write(tfd, p, q -p + 1) != q - p + 1)
247228545Sbapt				goto err;
248228545Sbapt			++q;
249228545Sbapt			continue;
250228545Sbapt		}
251228545Sbapt
252228545Sbapt		/* is it the one we're looking for? */
253228545Sbapt
254228545Sbapt		t = *q;
255228545Sbapt		*q = '\0';
256228545Sbapt
257228545Sbapt		fgr = gr_scan(r);
258228545Sbapt
259228545Sbapt		/* fgr is either a struct group for the current line,
260228545Sbapt		 * or NULL if the line is malformed.
261228545Sbapt		 */
262228545Sbapt
263228545Sbapt		*q = t;
264228545Sbapt		if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) {
265228545Sbapt			/* nope */
266228545Sbapt			if (fgr != NULL)
267228545Sbapt				free(fgr);
268228545Sbapt			if (write(tfd, p, q - p + 1) != q - p + 1)
269228545Sbapt				goto err;
270228545Sbapt			++q;
271228545Sbapt			continue;
272228545Sbapt		}
273228545Sbapt		if (old_gr && !gr_equal(fgr, old_gr)) {
274228545Sbapt			warnx("entry inconsistent");
275228545Sbapt			free(fgr);
276228545Sbapt			errno = EINVAL; /* hack */
277228545Sbapt			goto err;
278228545Sbapt		}
279228545Sbapt		free(fgr);
280228545Sbapt
281228545Sbapt		/* it is, replace or remove it */
282228545Sbapt		if (line != NULL) {
283228545Sbapt			len = strlen(line);
284228545Sbapt			if (write(tfd, line, len) != (int) len)
285228545Sbapt				goto err;
286228545Sbapt		} else {
287228545Sbapt			/* when removed, avoid the \n */
288228545Sbapt			q++;
289228545Sbapt		}
290228545Sbapt		/* we're done, just copy the rest over */
291228545Sbapt		for (;;) {
292228545Sbapt			if (write(tfd, q, end - q) != end - q)
293228545Sbapt				goto err;
294228545Sbapt			q = buf;
295310480Sdes			readlen = read(ffd, buf, size);
296228545Sbapt			if (readlen == 0)
297228545Sbapt				break;
298228545Sbapt			else
299228545Sbapt				len = (size_t)readlen;
300228545Sbapt			if (readlen == -1)
301228545Sbapt				goto err;
302228545Sbapt			end = buf + len;
303228545Sbapt		}
304228545Sbapt		goto done;
305228545Sbapt	}
306228545Sbapt
307228545Sbapt	/* if we got here, we didn't find the old entry */
308228545Sbapt	if (line == NULL) {
309228545Sbapt		errno = ENOENT;
310228545Sbapt		goto err;
311228545Sbapt	}
312228545Sbapt	len = strlen(line);
313228545Sbapt	if ((size_t)write(tfd, line, len) != len ||
314228545Sbapt	   write(tfd, "\n", 1) != 1)
315228545Sbapt		goto err;
316228545Sbapt done:
317310480Sdes	free(line);
318310480Sdes	free(buf);
319228545Sbapt	return (0);
320228545Sbapt err:
321310480Sdes	free(line);
322310480Sdes	free(buf);
323228545Sbapt	return (-1);
324228545Sbapt}
325228545Sbapt
326228545Sbapt/*
327228545Sbapt * Regenerate the group file
328228545Sbapt */
329228545Sbaptint
330228545Sbaptgr_mkdb(void)
331228545Sbapt{
332285205Sgarga	int fd;
333285205Sgarga
334243334Sbapt	if (chmod(tempname, 0644) != 0)
335243334Sbapt		return (-1);
336243328Sbapt
337285205Sgarga	if (rename(tempname, group_file) != 0)
338285205Sgarga		return (-1);
339285205Sgarga
340285205Sgarga	/*
341285205Sgarga	 * Make sure new group file is safe on disk. To improve performance we
342285205Sgarga	 * will call fsync() to the directory where file lies
343285205Sgarga	 */
344285205Sgarga	if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1)
345285205Sgarga		return (-1);
346285205Sgarga
347285205Sgarga	if (fsync(fd) != 0) {
348285205Sgarga		close(fd);
349285205Sgarga		return (-1);
350285205Sgarga	}
351285205Sgarga
352285205Sgarga	close(fd);
353285205Sgarga	return(0);
354228545Sbapt}
355228545Sbapt
356228545Sbapt/*
357245390Smjg * Clean up. Preserves errno for the caller's convenience.
358228545Sbapt */
359228545Sbaptvoid
360228545Sbaptgr_fini(void)
361228545Sbapt{
362228545Sbapt	int serrno;
363228545Sbapt
364228545Sbapt	if (!initialized)
365228545Sbapt		return;
366228545Sbapt	initialized = 0;
367228545Sbapt	serrno = errno;
368228545Sbapt	if (*tempname != '\0') {
369228545Sbapt		unlink(tempname);
370228545Sbapt		*tempname = '\0';
371228545Sbapt	}
372228545Sbapt	if (lockfd != -1)
373228545Sbapt		close(lockfd);
374228545Sbapt	errno = serrno;
375228545Sbapt}
376228545Sbapt
377228545Sbapt/*
378178431Sscf * Compares two struct group's.
379178431Sscf */
380178431Sscfint
381178431Sscfgr_equal(const struct group *gr1, const struct group *gr2)
382178431Sscf{
383185237Sscf	int gr1_ndx;
384185237Sscf	int gr2_ndx;
385178431Sscf
386178431Sscf	/* Check that the non-member information is the same. */
387185237Sscf	if (gr1->gr_name == NULL || gr2->gr_name == NULL) {
388185237Sscf		if (gr1->gr_name != gr2->gr_name)
389185237Sscf			return (false);
390185237Sscf	} else if (strcmp(gr1->gr_name, gr2->gr_name) != 0)
391185237Sscf		return (false);
392185237Sscf	if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) {
393185237Sscf		if (gr1->gr_passwd != gr2->gr_passwd)
394185237Sscf			return (false);
395185237Sscf	} else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0)
396185237Sscf		return (false);
397185237Sscf	if (gr1->gr_gid != gr2->gr_gid)
398185237Sscf		return (false);
399178431Sscf
400248102Sdb	/* Check all members in both groups.
401248102Sdb	 * getgrnam can return gr_mem with a pointer to NULL.
402248102Sdb	 * gr_dup and gr_add strip out this superfluous NULL, setting
403248102Sdb	 * gr_mem to NULL for no members.
404248102Sdb	*/
405248102Sdb	if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) {
406248102Sdb		int i;
407248102Sdb
408248102Sdb		for (i = 0; gr1->gr_mem[i] != NULL; i++) {
409248102Sdb			if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0)
410248102Sdb				return (false);
411178431Sscf		}
412178431Sscf	}
413248102Sdb	/* Count number of members in both structs */
414248102Sdb	gr2_ndx = 0;
415248102Sdb	if (gr2->gr_mem != NULL)
416248102Sdb		for(; gr2->gr_mem[gr2_ndx] != NULL; gr2_ndx++)
417248102Sdb			/* empty */;
418248102Sdb	gr1_ndx = 0;
419248102Sdb	if (gr1->gr_mem != NULL)
420248102Sdb		for(; gr1->gr_mem[gr1_ndx] != NULL; gr1_ndx++)
421248102Sdb			/* empty */;
422248102Sdb	if (gr1_ndx != gr2_ndx)
423248102Sdb		return (false);
424178431Sscf
425185237Sscf	return (true);
426178431Sscf}
427178431Sscf
428178431Sscf/*
429178431Sscf * Make a group line out of a struct group.
430178431Sscf */
431178431Sscfchar *
432178431Sscfgr_make(const struct group *gr)
433178431Sscf{
434245386Smjg	const char *group_line_format = "%s:%s:%ju:";
435245387Smjg	const char *sep;
436178431Sscf	char *line;
437245387Smjg	char *p;
438185237Sscf	size_t line_size;
439178431Sscf	int ndx;
440178431Sscf
441178431Sscf	/* Calculate the length of the group line. */
442185237Sscf	line_size = snprintf(NULL, 0, group_line_format, gr->gr_name,
443178431Sscf	    gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1;
444185237Sscf	if (gr->gr_mem != NULL) {
445185237Sscf		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++)
446185237Sscf			line_size += strlen(gr->gr_mem[ndx]) + 1;
447185237Sscf		if (ndx > 0)
448185237Sscf			line_size--;
449185237Sscf	}
450178431Sscf
451178431Sscf	/* Create the group line and fill it. */
452245387Smjg	if ((line = p = malloc(line_size)) == NULL)
453178431Sscf		return (NULL);
454245387Smjg	p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd,
455200423Sscf	    (uintmax_t)gr->gr_gid);
456245387Smjg	if (gr->gr_mem != NULL) {
457245387Smjg		sep = "";
458185237Sscf		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) {
459245387Smjg			p = stpcpy(p, sep);
460245387Smjg			p = stpcpy(p, gr->gr_mem[ndx]);
461245387Smjg			sep = ",";
462185237Sscf		}
463245387Smjg	}
464178431Sscf
465178431Sscf	return (line);
466178431Sscf}
467178431Sscf
468178431Sscf/*
469178431Sscf * Duplicate a struct group.
470178431Sscf */
471178431Sscfstruct group *
472178431Sscfgr_dup(const struct group *gr)
473178431Sscf{
474247919Sdb	return (gr_add(gr, NULL));
475247919Sdb}
476247919Sdb/*
477247919Sdb * Add a new member name to a struct group.
478247919Sdb */
479247919Sdbstruct group *
480247919Sdbgr_add(const struct group *gr, const char *newmember)
481247919Sdb{
482248102Sdb	char *mem;
483184831Sscf	size_t len;
484185237Sscf	int num_mem;
485178431Sscf
486247919Sdb	num_mem = 0;
487247919Sdb	len = grmemlen(gr, newmember, &num_mem);
488178431Sscf	/* Create new group and copy old group into it. */
489248102Sdb	if ((mem = malloc(len)) == NULL)
490178431Sscf		return (NULL);
491248102Sdb	return (grcopy(gr, mem, newmember, num_mem));
492247919Sdb}
493247919Sdb
494247919Sdb/* It is safer to walk the pointers given at gr_mem since there is no
495248102Sdb * guarantee the gr_mem + strings are contiguous in the given struct group
496248102Sdb * but compactify the new group into the following form.
497247919Sdb *
498247919Sdb * The new struct is laid out like this in memory. The example given is
499247919Sdb * for a group with two members only.
500247919Sdb *
501247919Sdb * {
502247919Sdb * (char *name)
503247919Sdb * (char *passwd)
504247919Sdb * (int gid)
505247919Sdb * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area
506247919Sdb * gr_mem area
507247919Sdb * (member1 *)
508247919Sdb * (member2 *)
509247919Sdb * (NULL)
510247919Sdb * (name string)
511247919Sdb * (passwd string)
512247919Sdb * (member1 string)
513247919Sdb * (member2 string)
514247919Sdb * }
515247919Sdb */
516247919Sdb/*
517248102Sdb * Copy the contents of a group plus given name to a preallocated group struct
518247919Sdb */
519247919Sdbstatic struct group *
520248102Sdbgrcopy(const struct group *gr, char *dst, const char *name, int ndx)
521247919Sdb{
522247919Sdb	int i;
523248102Sdb	struct group *newgr;
524247919Sdb
525248102Sdb	newgr = (struct group *)(void *)dst;	/* avoid alignment warning */
526248102Sdb	dst += sizeof(*newgr);
527248102Sdb	if (ndx != 0) {
528248102Sdb		newgr->gr_mem = (char **)(void *)(dst);	/* avoid alignment warning */
529248102Sdb		dst += (ndx + 1) * sizeof(*newgr->gr_mem);
530248102Sdb	} else
531244742Sbapt		newgr->gr_mem = NULL;
532178431Sscf	if (gr->gr_name != NULL) {
533244742Sbapt		newgr->gr_name = dst;
534244742Sbapt		dst = stpcpy(dst, gr->gr_name) + 1;
535247919Sdb	} else
536244777Sbapt		newgr->gr_name = NULL;
537178431Sscf	if (gr->gr_passwd != NULL) {
538244742Sbapt		newgr->gr_passwd = dst;
539244742Sbapt		dst = stpcpy(dst, gr->gr_passwd) + 1;
540247919Sdb	} else
541244777Sbapt		newgr->gr_passwd = NULL;
542244742Sbapt	newgr->gr_gid = gr->gr_gid;
543248102Sdb	i = 0;
544248102Sdb	/* Original group struct might have a NULL gr_mem */
545248102Sdb	if (gr->gr_mem != NULL) {
546248102Sdb		for (; gr->gr_mem[i] != NULL; i++) {
547247919Sdb			newgr->gr_mem[i] = dst;
548247919Sdb			dst = stpcpy(dst, gr->gr_mem[i]) + 1;
549178431Sscf		}
550248102Sdb	}
551248102Sdb	/* If name is not NULL, newgr->gr_mem is known to be not NULL */
552248102Sdb	if (name != NULL) {
553248102Sdb		newgr->gr_mem[i++] = dst;
554248102Sdb		dst = stpcpy(dst, name) + 1;
555248102Sdb	}
556248102Sdb	/* if newgr->gr_mem is not NULL add NULL marker */
557248102Sdb	if (newgr->gr_mem != NULL)
558247919Sdb		newgr->gr_mem[i] = NULL;
559248102Sdb
560244742Sbapt	return (newgr);
561178431Sscf}
562178431Sscf
563178431Sscf/*
564247919Sdb *  Calculate length of a struct group + given name
565244736Sbapt */
566247919Sdbstatic size_t
567247919Sdbgrmemlen(const struct group *gr, const char *name, int *num_mem)
568244736Sbapt{
569247919Sdb	size_t len;
570247919Sdb	int i;
571244736Sbapt
572247919Sdb	if (gr == NULL)
573247919Sdb		return (0);
574247919Sdb	/* Calculate size of the group. */
575247919Sdb	len = sizeof(*gr);
576247919Sdb	if (gr->gr_name != NULL)
577247919Sdb		len += strlen(gr->gr_name) + 1;
578247919Sdb	if (gr->gr_passwd != NULL)
579247919Sdb		len += strlen(gr->gr_passwd) + 1;
580248102Sdb	i = 0;
581244736Sbapt	if (gr->gr_mem != NULL) {
582248102Sdb		for (; gr->gr_mem[i] != NULL; i++) {
583247919Sdb			len += strlen(gr->gr_mem[i]) + 1;
584247919Sdb			len += sizeof(*gr->gr_mem);
585244736Sbapt		}
586244736Sbapt	}
587247919Sdb	if (name != NULL) {
588248102Sdb		i++;
589247919Sdb		len += strlen(name) + 1;
590248102Sdb		len += sizeof(*gr->gr_mem);
591247919Sdb	}
592248102Sdb	/* Allow for NULL pointer */
593248102Sdb	if (i != 0)
594248102Sdb		len += sizeof(*gr->gr_mem);
595248102Sdb	*num_mem = i;
596247919Sdb	return(len);
597244736Sbapt}
598244736Sbapt
599244736Sbapt/*
600178431Sscf * Scan a line and place it into a group structure.
601178431Sscf */
602178431Sscfstatic bool
603178431Sscf__gr_scan(char *line, struct group *gr)
604178431Sscf{
605178431Sscf	char *loc;
606178431Sscf	int ndx;
607178431Sscf
608178431Sscf	/* Assign non-member information to structure. */
609178431Sscf	gr->gr_name = line;
610178431Sscf	if ((loc = strchr(line, ':')) == NULL)
611178431Sscf		return (false);
612178431Sscf	*loc = '\0';
613178431Sscf	gr->gr_passwd = loc + 1;
614184831Sscf	if (*gr->gr_passwd == ':')
615184831Sscf		*gr->gr_passwd = '\0';
616178431Sscf	else {
617178431Sscf		if ((loc = strchr(loc + 1, ':')) == NULL)
618178431Sscf			return (false);
619178431Sscf		*loc = '\0';
620178431Sscf	}
621184831Sscf	if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1)
622178431Sscf		return (false);
623178431Sscf
624178431Sscf	/* Assign member information to structure. */
625178431Sscf	if ((loc = strchr(loc + 1, ':')) == NULL)
626178431Sscf		return (false);
627178431Sscf	line = loc + 1;
628178431Sscf	gr->gr_mem = NULL;
629185237Sscf	ndx = 0;
630185237Sscf	do {
631185237Sscf		gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) *
632185237Sscf		    (ndx + 1));
633185237Sscf		if (gr->gr_mem == NULL)
634185237Sscf			return (false);
635185237Sscf
636185237Sscf		/* Skip locations without members (i.e., empty string). */
637178431Sscf		do {
638178431Sscf			gr->gr_mem[ndx] = strsep(&line, ",");
639185237Sscf		} while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0');
640185237Sscf	} while (gr->gr_mem[ndx++] != NULL);
641178431Sscf
642178431Sscf	return (true);
643178431Sscf}
644178431Sscf
645178431Sscf/*
646178431Sscf * Create a struct group from a line.
647178431Sscf */
648178431Sscfstruct group *
649178431Sscfgr_scan(const char *line)
650178431Sscf{
651184831Sscf	struct group gr;
652185237Sscf	char *line_copy;
653185237Sscf	struct group *new_gr;
654178431Sscf
655185237Sscf	if ((line_copy = strdup(line)) == NULL)
656178431Sscf		return (NULL);
657185237Sscf	if (!__gr_scan(line_copy, &gr)) {
658185237Sscf		free(line_copy);
659178431Sscf		return (NULL);
660178431Sscf	}
661185237Sscf	new_gr = gr_dup(&gr);
662185237Sscf	free(line_copy);
663178431Sscf	if (gr.gr_mem != NULL)
664178431Sscf		free(gr.gr_mem);
665178431Sscf
666185237Sscf	return (new_gr);
667178431Sscf}
668