xref: /f-stack/tools/libutil/gr_util.c (revision 22ce4aff)
11eaf0ac3Slogwang /*-
2*22ce4affSfengbojiang  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang  *
41eaf0ac3Slogwang  * Copyright (c) 2008 Sean C. Farley <[email protected]>
51eaf0ac3Slogwang  * All rights reserved.
61eaf0ac3Slogwang  *
71eaf0ac3Slogwang  * Redistribution and use in source and binary forms, with or without
81eaf0ac3Slogwang  * modification, are permitted provided that the following conditions
91eaf0ac3Slogwang  * are met:
101eaf0ac3Slogwang  * 1. Redistributions of source code must retain the above copyright
111eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer,
121eaf0ac3Slogwang  *    without modification, immediately at the beginning of the file.
131eaf0ac3Slogwang  * 2. Redistributions in binary form must reproduce the above copyright
141eaf0ac3Slogwang  *    notice, this list of conditions and the following disclaimer in the
151eaf0ac3Slogwang  *    documentation and/or other materials provided with the distribution.
161eaf0ac3Slogwang  *
171eaf0ac3Slogwang  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
181eaf0ac3Slogwang  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
191eaf0ac3Slogwang  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
201eaf0ac3Slogwang  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
211eaf0ac3Slogwang  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
221eaf0ac3Slogwang  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
231eaf0ac3Slogwang  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
241eaf0ac3Slogwang  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
251eaf0ac3Slogwang  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
261eaf0ac3Slogwang  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
271eaf0ac3Slogwang  */
281eaf0ac3Slogwang 
291eaf0ac3Slogwang #include <sys/cdefs.h>
301eaf0ac3Slogwang __FBSDID("$FreeBSD$");
311eaf0ac3Slogwang 
321eaf0ac3Slogwang #include <sys/param.h>
331eaf0ac3Slogwang #include <sys/errno.h>
341eaf0ac3Slogwang #include <sys/stat.h>
351eaf0ac3Slogwang 
361eaf0ac3Slogwang #include <ctype.h>
371eaf0ac3Slogwang #include <err.h>
381eaf0ac3Slogwang #include <fcntl.h>
391eaf0ac3Slogwang #include <grp.h>
401eaf0ac3Slogwang #include <inttypes.h>
411eaf0ac3Slogwang #include <libutil.h>
421eaf0ac3Slogwang #include <paths.h>
431eaf0ac3Slogwang #include <stdbool.h>
441eaf0ac3Slogwang #include <stdio.h>
451eaf0ac3Slogwang #include <stdlib.h>
461eaf0ac3Slogwang #include <string.h>
471eaf0ac3Slogwang #include <unistd.h>
481eaf0ac3Slogwang 
491eaf0ac3Slogwang static int lockfd = -1;
501eaf0ac3Slogwang static char group_dir[PATH_MAX];
511eaf0ac3Slogwang static char group_file[PATH_MAX];
521eaf0ac3Slogwang static char tempname[PATH_MAX];
531eaf0ac3Slogwang static int initialized;
541eaf0ac3Slogwang static size_t grmemlen(const struct group *, const char *, int *);
551eaf0ac3Slogwang static struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx);
561eaf0ac3Slogwang 
571eaf0ac3Slogwang /*
581eaf0ac3Slogwang  * Initialize statics
591eaf0ac3Slogwang  */
601eaf0ac3Slogwang int
gr_init(const char * dir,const char * group)611eaf0ac3Slogwang gr_init(const char *dir, const char *group)
621eaf0ac3Slogwang {
631eaf0ac3Slogwang 
641eaf0ac3Slogwang 	if (dir == NULL) {
651eaf0ac3Slogwang 		strcpy(group_dir, _PATH_ETC);
661eaf0ac3Slogwang 	} else {
671eaf0ac3Slogwang 		if (strlen(dir) >= sizeof(group_dir)) {
681eaf0ac3Slogwang 			errno = ENAMETOOLONG;
691eaf0ac3Slogwang 			return (-1);
701eaf0ac3Slogwang 		}
711eaf0ac3Slogwang 		strcpy(group_dir, dir);
721eaf0ac3Slogwang 	}
731eaf0ac3Slogwang 
741eaf0ac3Slogwang 	if (group == NULL) {
751eaf0ac3Slogwang 		if (dir == NULL) {
761eaf0ac3Slogwang 			strcpy(group_file, _PATH_GROUP);
771eaf0ac3Slogwang 		} else if (snprintf(group_file, sizeof(group_file), "%s/group",
781eaf0ac3Slogwang 			group_dir) > (int)sizeof(group_file)) {
791eaf0ac3Slogwang 			errno = ENAMETOOLONG;
801eaf0ac3Slogwang 			return (-1);
811eaf0ac3Slogwang 		}
821eaf0ac3Slogwang 	} else {
831eaf0ac3Slogwang 		if (strlen(group) >= sizeof(group_file)) {
841eaf0ac3Slogwang 			errno = ENAMETOOLONG;
851eaf0ac3Slogwang 			return (-1);
861eaf0ac3Slogwang 		}
871eaf0ac3Slogwang 		strcpy(group_file, group);
881eaf0ac3Slogwang 	}
891eaf0ac3Slogwang 
901eaf0ac3Slogwang 	initialized = 1;
911eaf0ac3Slogwang 	return (0);
921eaf0ac3Slogwang }
931eaf0ac3Slogwang 
941eaf0ac3Slogwang /*
951eaf0ac3Slogwang  * Lock the group file
961eaf0ac3Slogwang  */
971eaf0ac3Slogwang int
gr_lock(void)981eaf0ac3Slogwang gr_lock(void)
991eaf0ac3Slogwang {
1001eaf0ac3Slogwang 	if (*group_file == '\0')
1011eaf0ac3Slogwang 		return (-1);
1021eaf0ac3Slogwang 
1031eaf0ac3Slogwang 	for (;;) {
1041eaf0ac3Slogwang 		struct stat st;
1051eaf0ac3Slogwang 
1061eaf0ac3Slogwang 		lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0);
1071eaf0ac3Slogwang 		if (lockfd == -1) {
1081eaf0ac3Slogwang 			if (errno == EWOULDBLOCK) {
1091eaf0ac3Slogwang 				errx(1, "the group file is busy");
1101eaf0ac3Slogwang 			} else {
111*22ce4affSfengbojiang 				err(1, "could not lock the group file");
1121eaf0ac3Slogwang 			}
1131eaf0ac3Slogwang 		}
1141eaf0ac3Slogwang 		if (fstat(lockfd, &st) == -1)
115*22ce4affSfengbojiang 			err(1, "fstat() failed");
1161eaf0ac3Slogwang 		if (st.st_nlink != 0)
1171eaf0ac3Slogwang 			break;
1181eaf0ac3Slogwang 		close(lockfd);
1191eaf0ac3Slogwang 		lockfd = -1;
1201eaf0ac3Slogwang 	}
1211eaf0ac3Slogwang 	return (lockfd);
1221eaf0ac3Slogwang }
1231eaf0ac3Slogwang 
1241eaf0ac3Slogwang /*
1251eaf0ac3Slogwang  * Create and open a presmuably safe temp file for editing group data
1261eaf0ac3Slogwang  */
1271eaf0ac3Slogwang int
gr_tmp(int mfd)1281eaf0ac3Slogwang gr_tmp(int mfd)
1291eaf0ac3Slogwang {
1301eaf0ac3Slogwang 	char buf[8192];
1311eaf0ac3Slogwang 	ssize_t nr;
1321eaf0ac3Slogwang 	const char *p;
1331eaf0ac3Slogwang 	int tfd;
1341eaf0ac3Slogwang 
1351eaf0ac3Slogwang 	if (*group_file == '\0')
1361eaf0ac3Slogwang 		return (-1);
1371eaf0ac3Slogwang 	if ((p = strrchr(group_file, '/')))
1381eaf0ac3Slogwang 		++p;
1391eaf0ac3Slogwang 	else
1401eaf0ac3Slogwang 		p = group_file;
1411eaf0ac3Slogwang 	if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX",
1421eaf0ac3Slogwang 		(int)(p - group_file), group_file) >= (int)sizeof(tempname)) {
1431eaf0ac3Slogwang 		errno = ENAMETOOLONG;
1441eaf0ac3Slogwang 		return (-1);
1451eaf0ac3Slogwang 	}
146*22ce4affSfengbojiang 	if ((tfd = mkostemp(tempname, 0)) == -1)
1471eaf0ac3Slogwang 		return (-1);
1481eaf0ac3Slogwang 	if (mfd != -1) {
1491eaf0ac3Slogwang 		while ((nr = read(mfd, buf, sizeof(buf))) > 0)
1501eaf0ac3Slogwang 			if (write(tfd, buf, (size_t)nr) != nr)
1511eaf0ac3Slogwang 				break;
1521eaf0ac3Slogwang 		if (nr != 0) {
1531eaf0ac3Slogwang 			unlink(tempname);
1541eaf0ac3Slogwang 			*tempname = '\0';
1551eaf0ac3Slogwang 			close(tfd);
1561eaf0ac3Slogwang 			return (-1);
1571eaf0ac3Slogwang 		}
1581eaf0ac3Slogwang 	}
1591eaf0ac3Slogwang 	return (tfd);
1601eaf0ac3Slogwang }
1611eaf0ac3Slogwang 
1621eaf0ac3Slogwang /*
1631eaf0ac3Slogwang  * Copy the group file from one descriptor to another, replacing, deleting
1641eaf0ac3Slogwang  * or adding a single record on the way.
1651eaf0ac3Slogwang  */
1661eaf0ac3Slogwang int
gr_copy(int ffd,int tfd,const struct group * gr,struct group * old_gr)1671eaf0ac3Slogwang gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr)
1681eaf0ac3Slogwang {
169*22ce4affSfengbojiang 	char *buf, *end, *line, *p, *q, *r, *tmp;
1701eaf0ac3Slogwang 	struct group *fgr;
1711eaf0ac3Slogwang 	const struct group *sgr;
172*22ce4affSfengbojiang 	size_t len, size;
1731eaf0ac3Slogwang 	int eof, readlen;
174*22ce4affSfengbojiang 	char t;
1751eaf0ac3Slogwang 
1761eaf0ac3Slogwang 	if (old_gr == NULL && gr == NULL)
1771eaf0ac3Slogwang 		return(-1);
1781eaf0ac3Slogwang 
1791eaf0ac3Slogwang 	sgr = old_gr;
1801eaf0ac3Slogwang 	/* deleting a group */
1811eaf0ac3Slogwang 	if (gr == NULL) {
1821eaf0ac3Slogwang 		line = NULL;
1831eaf0ac3Slogwang 	} else {
1841eaf0ac3Slogwang 		if ((line = gr_make(gr)) == NULL)
1851eaf0ac3Slogwang 			return (-1);
1861eaf0ac3Slogwang 	}
1871eaf0ac3Slogwang 
1881eaf0ac3Slogwang 	/* adding a group */
1891eaf0ac3Slogwang 	if (sgr == NULL)
1901eaf0ac3Slogwang 		sgr = gr;
1911eaf0ac3Slogwang 
192*22ce4affSfengbojiang 	/* initialize the buffer */
193*22ce4affSfengbojiang 	if ((buf = malloc(size = 1024)) == NULL)
194*22ce4affSfengbojiang 		goto err;
195*22ce4affSfengbojiang 
1961eaf0ac3Slogwang 	eof = 0;
1971eaf0ac3Slogwang 	len = 0;
1981eaf0ac3Slogwang 	p = q = end = buf;
1991eaf0ac3Slogwang 	for (;;) {
2001eaf0ac3Slogwang 		/* find the end of the current line */
2011eaf0ac3Slogwang 		for (p = q; q < end && *q != '\0'; ++q)
2021eaf0ac3Slogwang 			if (*q == '\n')
2031eaf0ac3Slogwang 				break;
2041eaf0ac3Slogwang 
2051eaf0ac3Slogwang 		/* if we don't have a complete line, fill up the buffer */
2061eaf0ac3Slogwang 		if (q >= end) {
2071eaf0ac3Slogwang 			if (eof)
2081eaf0ac3Slogwang 				break;
209*22ce4affSfengbojiang 			while ((size_t)(q - p) >= size) {
210*22ce4affSfengbojiang 				if ((tmp = reallocarray(buf, 2, size)) == NULL) {
2111eaf0ac3Slogwang 					warnx("group line too long");
2121eaf0ac3Slogwang 					goto err;
2131eaf0ac3Slogwang 				}
214*22ce4affSfengbojiang 				p = tmp + (p - buf);
215*22ce4affSfengbojiang 				q = tmp + (q - buf);
216*22ce4affSfengbojiang 				end = tmp + (end - buf);
217*22ce4affSfengbojiang 				buf = tmp;
218*22ce4affSfengbojiang 				size = size * 2;
219*22ce4affSfengbojiang 			}
2201eaf0ac3Slogwang 			if (p < end) {
2211eaf0ac3Slogwang 				q = memmove(buf, p, end -p);
2221eaf0ac3Slogwang 				end -= p - buf;
2231eaf0ac3Slogwang 			} else {
2241eaf0ac3Slogwang 				p = q = end = buf;
2251eaf0ac3Slogwang 			}
226*22ce4affSfengbojiang 			readlen = read(ffd, end, size - (end - buf));
2271eaf0ac3Slogwang 			if (readlen == -1)
2281eaf0ac3Slogwang 				goto err;
2291eaf0ac3Slogwang 			else
2301eaf0ac3Slogwang 				len = (size_t)readlen;
2311eaf0ac3Slogwang 			if (len == 0 && p == buf)
2321eaf0ac3Slogwang 				break;
2331eaf0ac3Slogwang 			end += len;
2341eaf0ac3Slogwang 			len = end - buf;
235*22ce4affSfengbojiang 			if (len < size) {
2361eaf0ac3Slogwang 				eof = 1;
2371eaf0ac3Slogwang 				if (len > 0 && buf[len -1] != '\n')
2381eaf0ac3Slogwang 					++len, *end++ = '\n';
2391eaf0ac3Slogwang 			}
2401eaf0ac3Slogwang 			continue;
2411eaf0ac3Slogwang 		}
2421eaf0ac3Slogwang 
2431eaf0ac3Slogwang 		/* is it a blank line or a comment? */
2441eaf0ac3Slogwang 		for (r = p; r < q && isspace(*r); ++r)
2451eaf0ac3Slogwang 			/* nothing */;
2461eaf0ac3Slogwang 		if (r == q || *r == '#') {
2471eaf0ac3Slogwang 			/* yep */
2481eaf0ac3Slogwang 			if (write(tfd, p, q -p + 1) != q - p + 1)
2491eaf0ac3Slogwang 				goto err;
2501eaf0ac3Slogwang 			++q;
2511eaf0ac3Slogwang 			continue;
2521eaf0ac3Slogwang 		}
2531eaf0ac3Slogwang 
2541eaf0ac3Slogwang 		/* is it the one we're looking for? */
2551eaf0ac3Slogwang 
2561eaf0ac3Slogwang 		t = *q;
2571eaf0ac3Slogwang 		*q = '\0';
2581eaf0ac3Slogwang 
2591eaf0ac3Slogwang 		fgr = gr_scan(r);
2601eaf0ac3Slogwang 
2611eaf0ac3Slogwang 		/* fgr is either a struct group for the current line,
2621eaf0ac3Slogwang 		 * or NULL if the line is malformed.
2631eaf0ac3Slogwang 		 */
2641eaf0ac3Slogwang 
2651eaf0ac3Slogwang 		*q = t;
2661eaf0ac3Slogwang 		if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) {
2671eaf0ac3Slogwang 			/* nope */
2681eaf0ac3Slogwang 			if (fgr != NULL)
2691eaf0ac3Slogwang 				free(fgr);
2701eaf0ac3Slogwang 			if (write(tfd, p, q - p + 1) != q - p + 1)
2711eaf0ac3Slogwang 				goto err;
2721eaf0ac3Slogwang 			++q;
2731eaf0ac3Slogwang 			continue;
2741eaf0ac3Slogwang 		}
2751eaf0ac3Slogwang 		if (old_gr && !gr_equal(fgr, old_gr)) {
2761eaf0ac3Slogwang 			warnx("entry inconsistent");
2771eaf0ac3Slogwang 			free(fgr);
2781eaf0ac3Slogwang 			errno = EINVAL; /* hack */
2791eaf0ac3Slogwang 			goto err;
2801eaf0ac3Slogwang 		}
2811eaf0ac3Slogwang 		free(fgr);
2821eaf0ac3Slogwang 
2831eaf0ac3Slogwang 		/* it is, replace or remove it */
2841eaf0ac3Slogwang 		if (line != NULL) {
2851eaf0ac3Slogwang 			len = strlen(line);
2861eaf0ac3Slogwang 			if (write(tfd, line, len) != (int) len)
2871eaf0ac3Slogwang 				goto err;
2881eaf0ac3Slogwang 		} else {
2891eaf0ac3Slogwang 			/* when removed, avoid the \n */
2901eaf0ac3Slogwang 			q++;
2911eaf0ac3Slogwang 		}
2921eaf0ac3Slogwang 		/* we're done, just copy the rest over */
2931eaf0ac3Slogwang 		for (;;) {
2941eaf0ac3Slogwang 			if (write(tfd, q, end - q) != end - q)
2951eaf0ac3Slogwang 				goto err;
2961eaf0ac3Slogwang 			q = buf;
297*22ce4affSfengbojiang 			readlen = read(ffd, buf, size);
2981eaf0ac3Slogwang 			if (readlen == 0)
2991eaf0ac3Slogwang 				break;
3001eaf0ac3Slogwang 			else
3011eaf0ac3Slogwang 				len = (size_t)readlen;
3021eaf0ac3Slogwang 			if (readlen == -1)
3031eaf0ac3Slogwang 				goto err;
3041eaf0ac3Slogwang 			end = buf + len;
3051eaf0ac3Slogwang 		}
3061eaf0ac3Slogwang 		goto done;
3071eaf0ac3Slogwang 	}
3081eaf0ac3Slogwang 
3091eaf0ac3Slogwang 	/* if we got here, we didn't find the old entry */
3101eaf0ac3Slogwang 	if (line == NULL) {
3111eaf0ac3Slogwang 		errno = ENOENT;
3121eaf0ac3Slogwang 		goto err;
3131eaf0ac3Slogwang 	}
3141eaf0ac3Slogwang 	len = strlen(line);
3151eaf0ac3Slogwang 	if ((size_t)write(tfd, line, len) != len ||
3161eaf0ac3Slogwang 	   write(tfd, "\n", 1) != 1)
3171eaf0ac3Slogwang 		goto err;
3181eaf0ac3Slogwang  done:
3191eaf0ac3Slogwang 	free(line);
320*22ce4affSfengbojiang 	free(buf);
3211eaf0ac3Slogwang 	return (0);
3221eaf0ac3Slogwang  err:
3231eaf0ac3Slogwang 	free(line);
324*22ce4affSfengbojiang 	free(buf);
3251eaf0ac3Slogwang 	return (-1);
3261eaf0ac3Slogwang }
3271eaf0ac3Slogwang 
3281eaf0ac3Slogwang /*
3291eaf0ac3Slogwang  * Regenerate the group file
3301eaf0ac3Slogwang  */
3311eaf0ac3Slogwang int
gr_mkdb(void)3321eaf0ac3Slogwang gr_mkdb(void)
3331eaf0ac3Slogwang {
3341eaf0ac3Slogwang 	int fd;
3351eaf0ac3Slogwang 
3361eaf0ac3Slogwang 	if (chmod(tempname, 0644) != 0)
3371eaf0ac3Slogwang 		return (-1);
3381eaf0ac3Slogwang 
3391eaf0ac3Slogwang 	if (rename(tempname, group_file) != 0)
3401eaf0ac3Slogwang 		return (-1);
3411eaf0ac3Slogwang 
3421eaf0ac3Slogwang 	/*
3431eaf0ac3Slogwang 	 * Make sure new group file is safe on disk. To improve performance we
3441eaf0ac3Slogwang 	 * will call fsync() to the directory where file lies
3451eaf0ac3Slogwang 	 */
3461eaf0ac3Slogwang 	if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1)
3471eaf0ac3Slogwang 		return (-1);
3481eaf0ac3Slogwang 
3491eaf0ac3Slogwang 	if (fsync(fd) != 0) {
3501eaf0ac3Slogwang 		close(fd);
3511eaf0ac3Slogwang 		return (-1);
3521eaf0ac3Slogwang 	}
3531eaf0ac3Slogwang 
3541eaf0ac3Slogwang 	close(fd);
3551eaf0ac3Slogwang 	return(0);
3561eaf0ac3Slogwang }
3571eaf0ac3Slogwang 
3581eaf0ac3Slogwang /*
3591eaf0ac3Slogwang  * Clean up. Preserves errno for the caller's convenience.
3601eaf0ac3Slogwang  */
3611eaf0ac3Slogwang void
gr_fini(void)3621eaf0ac3Slogwang gr_fini(void)
3631eaf0ac3Slogwang {
3641eaf0ac3Slogwang 	int serrno;
3651eaf0ac3Slogwang 
3661eaf0ac3Slogwang 	if (!initialized)
3671eaf0ac3Slogwang 		return;
3681eaf0ac3Slogwang 	initialized = 0;
3691eaf0ac3Slogwang 	serrno = errno;
3701eaf0ac3Slogwang 	if (*tempname != '\0') {
3711eaf0ac3Slogwang 		unlink(tempname);
3721eaf0ac3Slogwang 		*tempname = '\0';
3731eaf0ac3Slogwang 	}
3741eaf0ac3Slogwang 	if (lockfd != -1)
3751eaf0ac3Slogwang 		close(lockfd);
3761eaf0ac3Slogwang 	errno = serrno;
3771eaf0ac3Slogwang }
3781eaf0ac3Slogwang 
3791eaf0ac3Slogwang /*
3801eaf0ac3Slogwang  * Compares two struct group's.
3811eaf0ac3Slogwang  */
3821eaf0ac3Slogwang int
gr_equal(const struct group * gr1,const struct group * gr2)3831eaf0ac3Slogwang gr_equal(const struct group *gr1, const struct group *gr2)
3841eaf0ac3Slogwang {
3851eaf0ac3Slogwang 
3861eaf0ac3Slogwang 	/* Check that the non-member information is the same. */
3871eaf0ac3Slogwang 	if (gr1->gr_name == NULL || gr2->gr_name == NULL) {
3881eaf0ac3Slogwang 		if (gr1->gr_name != gr2->gr_name)
3891eaf0ac3Slogwang 			return (false);
3901eaf0ac3Slogwang 	} else if (strcmp(gr1->gr_name, gr2->gr_name) != 0)
3911eaf0ac3Slogwang 		return (false);
3921eaf0ac3Slogwang 	if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) {
3931eaf0ac3Slogwang 		if (gr1->gr_passwd != gr2->gr_passwd)
3941eaf0ac3Slogwang 			return (false);
3951eaf0ac3Slogwang 	} else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0)
3961eaf0ac3Slogwang 		return (false);
3971eaf0ac3Slogwang 	if (gr1->gr_gid != gr2->gr_gid)
3981eaf0ac3Slogwang 		return (false);
3991eaf0ac3Slogwang 
4001eaf0ac3Slogwang 	/*
4011eaf0ac3Slogwang 	 * Check all members in both groups.
4021eaf0ac3Slogwang 	 * getgrnam can return gr_mem with a pointer to NULL.
4031eaf0ac3Slogwang 	 * gr_dup and gr_add strip out this superfluous NULL, setting
4041eaf0ac3Slogwang 	 * gr_mem to NULL for no members.
4051eaf0ac3Slogwang 	*/
4061eaf0ac3Slogwang 	if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) {
4071eaf0ac3Slogwang 		int i;
4081eaf0ac3Slogwang 
4091eaf0ac3Slogwang 		for (i = 0;
4101eaf0ac3Slogwang 		    gr1->gr_mem[i] != NULL && gr2->gr_mem[i] != NULL; i++) {
4111eaf0ac3Slogwang 			if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0)
4121eaf0ac3Slogwang 				return (false);
4131eaf0ac3Slogwang 		}
4141eaf0ac3Slogwang 		if (gr1->gr_mem[i] != NULL || gr2->gr_mem[i] != NULL)
4151eaf0ac3Slogwang 			return (false);
4161eaf0ac3Slogwang 	} else if (gr1->gr_mem != NULL && gr1->gr_mem[0] != NULL) {
4171eaf0ac3Slogwang 		return (false);
4181eaf0ac3Slogwang 	} else if (gr2->gr_mem != NULL && gr2->gr_mem[0] != NULL) {
4191eaf0ac3Slogwang 		return (false);
4201eaf0ac3Slogwang 	}
4211eaf0ac3Slogwang 
4221eaf0ac3Slogwang 	return (true);
4231eaf0ac3Slogwang }
4241eaf0ac3Slogwang 
4251eaf0ac3Slogwang /*
4261eaf0ac3Slogwang  * Make a group line out of a struct group.
4271eaf0ac3Slogwang  */
4281eaf0ac3Slogwang char *
gr_make(const struct group * gr)4291eaf0ac3Slogwang gr_make(const struct group *gr)
4301eaf0ac3Slogwang {
4311eaf0ac3Slogwang 	const char *group_line_format = "%s:%s:%ju:";
4321eaf0ac3Slogwang 	const char *sep;
4331eaf0ac3Slogwang 	char *line;
4341eaf0ac3Slogwang 	char *p;
4351eaf0ac3Slogwang 	size_t line_size;
4361eaf0ac3Slogwang 	int ndx;
4371eaf0ac3Slogwang 
4381eaf0ac3Slogwang 	/* Calculate the length of the group line. */
4391eaf0ac3Slogwang 	line_size = snprintf(NULL, 0, group_line_format, gr->gr_name,
4401eaf0ac3Slogwang 	    gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1;
4411eaf0ac3Slogwang 	if (gr->gr_mem != NULL) {
4421eaf0ac3Slogwang 		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++)
4431eaf0ac3Slogwang 			line_size += strlen(gr->gr_mem[ndx]) + 1;
4441eaf0ac3Slogwang 		if (ndx > 0)
4451eaf0ac3Slogwang 			line_size--;
4461eaf0ac3Slogwang 	}
4471eaf0ac3Slogwang 
4481eaf0ac3Slogwang 	/* Create the group line and fill it. */
4491eaf0ac3Slogwang 	if ((line = p = malloc(line_size)) == NULL)
4501eaf0ac3Slogwang 		return (NULL);
4511eaf0ac3Slogwang 	p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd,
4521eaf0ac3Slogwang 	    (uintmax_t)gr->gr_gid);
4531eaf0ac3Slogwang 	if (gr->gr_mem != NULL) {
4541eaf0ac3Slogwang 		sep = "";
4551eaf0ac3Slogwang 		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) {
4561eaf0ac3Slogwang 			p = stpcpy(p, sep);
4571eaf0ac3Slogwang 			p = stpcpy(p, gr->gr_mem[ndx]);
4581eaf0ac3Slogwang 			sep = ",";
4591eaf0ac3Slogwang 		}
4601eaf0ac3Slogwang 	}
4611eaf0ac3Slogwang 
4621eaf0ac3Slogwang 	return (line);
4631eaf0ac3Slogwang }
4641eaf0ac3Slogwang 
4651eaf0ac3Slogwang /*
4661eaf0ac3Slogwang  * Duplicate a struct group.
4671eaf0ac3Slogwang  */
4681eaf0ac3Slogwang struct group *
gr_dup(const struct group * gr)4691eaf0ac3Slogwang gr_dup(const struct group *gr)
4701eaf0ac3Slogwang {
4711eaf0ac3Slogwang 	return (gr_add(gr, NULL));
4721eaf0ac3Slogwang }
4731eaf0ac3Slogwang /*
4741eaf0ac3Slogwang  * Add a new member name to a struct group.
4751eaf0ac3Slogwang  */
4761eaf0ac3Slogwang struct group *
gr_add(const struct group * gr,const char * newmember)4771eaf0ac3Slogwang gr_add(const struct group *gr, const char *newmember)
4781eaf0ac3Slogwang {
4791eaf0ac3Slogwang 	char *mem;
4801eaf0ac3Slogwang 	size_t len;
4811eaf0ac3Slogwang 	int num_mem;
4821eaf0ac3Slogwang 
4831eaf0ac3Slogwang 	num_mem = 0;
4841eaf0ac3Slogwang 	len = grmemlen(gr, newmember, &num_mem);
4851eaf0ac3Slogwang 	/* Create new group and copy old group into it. */
4861eaf0ac3Slogwang 	if ((mem = malloc(len)) == NULL)
4871eaf0ac3Slogwang 		return (NULL);
4881eaf0ac3Slogwang 	return (grcopy(gr, mem, newmember, num_mem));
4891eaf0ac3Slogwang }
4901eaf0ac3Slogwang 
4911eaf0ac3Slogwang /* It is safer to walk the pointers given at gr_mem since there is no
4921eaf0ac3Slogwang  * guarantee the gr_mem + strings are contiguous in the given struct group
4931eaf0ac3Slogwang  * but compactify the new group into the following form.
4941eaf0ac3Slogwang  *
4951eaf0ac3Slogwang  * The new struct is laid out like this in memory. The example given is
4961eaf0ac3Slogwang  * for a group with two members only.
4971eaf0ac3Slogwang  *
4981eaf0ac3Slogwang  * {
4991eaf0ac3Slogwang  * (char *name)
5001eaf0ac3Slogwang  * (char *passwd)
5011eaf0ac3Slogwang  * (int gid)
5021eaf0ac3Slogwang  * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area
5031eaf0ac3Slogwang  * gr_mem area
5041eaf0ac3Slogwang  * (member1 *)
5051eaf0ac3Slogwang  * (member2 *)
5061eaf0ac3Slogwang  * (NULL)
5071eaf0ac3Slogwang  * (name string)
5081eaf0ac3Slogwang  * (passwd string)
5091eaf0ac3Slogwang  * (member1 string)
5101eaf0ac3Slogwang  * (member2 string)
5111eaf0ac3Slogwang  * }
5121eaf0ac3Slogwang  */
5131eaf0ac3Slogwang /*
5141eaf0ac3Slogwang  * Copy the contents of a group plus given name to a preallocated group struct
5151eaf0ac3Slogwang  */
5161eaf0ac3Slogwang static struct group *
grcopy(const struct group * gr,char * dst,const char * name,int ndx)5171eaf0ac3Slogwang grcopy(const struct group *gr, char *dst, const char *name, int ndx)
5181eaf0ac3Slogwang {
5191eaf0ac3Slogwang 	int i;
5201eaf0ac3Slogwang 	struct group *newgr;
5211eaf0ac3Slogwang 
5221eaf0ac3Slogwang 	newgr = (struct group *)(void *)dst;	/* avoid alignment warning */
5231eaf0ac3Slogwang 	dst += sizeof(*newgr);
5241eaf0ac3Slogwang 	if (ndx != 0) {
5251eaf0ac3Slogwang 		newgr->gr_mem = (char **)(void *)(dst);	/* avoid alignment warning */
5261eaf0ac3Slogwang 		dst += (ndx + 1) * sizeof(*newgr->gr_mem);
5271eaf0ac3Slogwang 	} else
5281eaf0ac3Slogwang 		newgr->gr_mem = NULL;
5291eaf0ac3Slogwang 	if (gr->gr_name != NULL) {
5301eaf0ac3Slogwang 		newgr->gr_name = dst;
5311eaf0ac3Slogwang 		dst = stpcpy(dst, gr->gr_name) + 1;
5321eaf0ac3Slogwang 	} else
5331eaf0ac3Slogwang 		newgr->gr_name = NULL;
5341eaf0ac3Slogwang 	if (gr->gr_passwd != NULL) {
5351eaf0ac3Slogwang 		newgr->gr_passwd = dst;
5361eaf0ac3Slogwang 		dst = stpcpy(dst, gr->gr_passwd) + 1;
5371eaf0ac3Slogwang 	} else
5381eaf0ac3Slogwang 		newgr->gr_passwd = NULL;
5391eaf0ac3Slogwang 	newgr->gr_gid = gr->gr_gid;
5401eaf0ac3Slogwang 	i = 0;
5411eaf0ac3Slogwang 	/* Original group struct might have a NULL gr_mem */
5421eaf0ac3Slogwang 	if (gr->gr_mem != NULL) {
5431eaf0ac3Slogwang 		for (; gr->gr_mem[i] != NULL; i++) {
5441eaf0ac3Slogwang 			newgr->gr_mem[i] = dst;
5451eaf0ac3Slogwang 			dst = stpcpy(dst, gr->gr_mem[i]) + 1;
5461eaf0ac3Slogwang 		}
5471eaf0ac3Slogwang 	}
5481eaf0ac3Slogwang 	/* If name is not NULL, newgr->gr_mem is known to be not NULL */
5491eaf0ac3Slogwang 	if (name != NULL) {
5501eaf0ac3Slogwang 		newgr->gr_mem[i++] = dst;
5511eaf0ac3Slogwang 		dst = stpcpy(dst, name) + 1;
5521eaf0ac3Slogwang 	}
5531eaf0ac3Slogwang 	/* if newgr->gr_mem is not NULL add NULL marker */
5541eaf0ac3Slogwang 	if (newgr->gr_mem != NULL)
5551eaf0ac3Slogwang 		newgr->gr_mem[i] = NULL;
5561eaf0ac3Slogwang 
5571eaf0ac3Slogwang 	return (newgr);
5581eaf0ac3Slogwang }
5591eaf0ac3Slogwang 
5601eaf0ac3Slogwang /*
5611eaf0ac3Slogwang  *  Calculate length of a struct group + given name
5621eaf0ac3Slogwang  */
5631eaf0ac3Slogwang static size_t
grmemlen(const struct group * gr,const char * name,int * num_mem)5641eaf0ac3Slogwang grmemlen(const struct group *gr, const char *name, int *num_mem)
5651eaf0ac3Slogwang {
5661eaf0ac3Slogwang 	size_t len;
5671eaf0ac3Slogwang 	int i;
5681eaf0ac3Slogwang 
5691eaf0ac3Slogwang 	if (gr == NULL)
5701eaf0ac3Slogwang 		return (0);
5711eaf0ac3Slogwang 	/* Calculate size of the group. */
5721eaf0ac3Slogwang 	len = sizeof(*gr);
5731eaf0ac3Slogwang 	if (gr->gr_name != NULL)
5741eaf0ac3Slogwang 		len += strlen(gr->gr_name) + 1;
5751eaf0ac3Slogwang 	if (gr->gr_passwd != NULL)
5761eaf0ac3Slogwang 		len += strlen(gr->gr_passwd) + 1;
5771eaf0ac3Slogwang 	i = 0;
5781eaf0ac3Slogwang 	if (gr->gr_mem != NULL) {
5791eaf0ac3Slogwang 		for (; gr->gr_mem[i] != NULL; i++) {
5801eaf0ac3Slogwang 			len += strlen(gr->gr_mem[i]) + 1;
5811eaf0ac3Slogwang 			len += sizeof(*gr->gr_mem);
5821eaf0ac3Slogwang 		}
5831eaf0ac3Slogwang 	}
5841eaf0ac3Slogwang 	if (name != NULL) {
5851eaf0ac3Slogwang 		i++;
5861eaf0ac3Slogwang 		len += strlen(name) + 1;
5871eaf0ac3Slogwang 		len += sizeof(*gr->gr_mem);
5881eaf0ac3Slogwang 	}
5891eaf0ac3Slogwang 	/* Allow for NULL pointer */
5901eaf0ac3Slogwang 	if (i != 0)
5911eaf0ac3Slogwang 		len += sizeof(*gr->gr_mem);
5921eaf0ac3Slogwang 	*num_mem = i;
5931eaf0ac3Slogwang 	return(len);
5941eaf0ac3Slogwang }
5951eaf0ac3Slogwang 
5961eaf0ac3Slogwang /*
5971eaf0ac3Slogwang  * Scan a line and place it into a group structure.
5981eaf0ac3Slogwang  */
5991eaf0ac3Slogwang static bool
__gr_scan(char * line,struct group * gr)6001eaf0ac3Slogwang __gr_scan(char *line, struct group *gr)
6011eaf0ac3Slogwang {
6021eaf0ac3Slogwang 	char *loc;
6031eaf0ac3Slogwang 	int ndx;
6041eaf0ac3Slogwang 
6051eaf0ac3Slogwang 	/* Assign non-member information to structure. */
6061eaf0ac3Slogwang 	gr->gr_name = line;
6071eaf0ac3Slogwang 	if ((loc = strchr(line, ':')) == NULL)
6081eaf0ac3Slogwang 		return (false);
6091eaf0ac3Slogwang 	*loc = '\0';
6101eaf0ac3Slogwang 	gr->gr_passwd = loc + 1;
6111eaf0ac3Slogwang 	if (*gr->gr_passwd == ':')
6121eaf0ac3Slogwang 		*gr->gr_passwd = '\0';
6131eaf0ac3Slogwang 	else {
6141eaf0ac3Slogwang 		if ((loc = strchr(loc + 1, ':')) == NULL)
6151eaf0ac3Slogwang 			return (false);
6161eaf0ac3Slogwang 		*loc = '\0';
6171eaf0ac3Slogwang 	}
6181eaf0ac3Slogwang 	if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1)
6191eaf0ac3Slogwang 		return (false);
6201eaf0ac3Slogwang 
6211eaf0ac3Slogwang 	/* Assign member information to structure. */
6221eaf0ac3Slogwang 	if ((loc = strchr(loc + 1, ':')) == NULL)
6231eaf0ac3Slogwang 		return (false);
6241eaf0ac3Slogwang 	line = loc + 1;
6251eaf0ac3Slogwang 	gr->gr_mem = NULL;
6261eaf0ac3Slogwang 	ndx = 0;
6271eaf0ac3Slogwang 	do {
6281eaf0ac3Slogwang 		gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) *
6291eaf0ac3Slogwang 		    (ndx + 1));
6301eaf0ac3Slogwang 		if (gr->gr_mem == NULL)
6311eaf0ac3Slogwang 			return (false);
6321eaf0ac3Slogwang 
6331eaf0ac3Slogwang 		/* Skip locations without members (i.e., empty string). */
6341eaf0ac3Slogwang 		do {
6351eaf0ac3Slogwang 			gr->gr_mem[ndx] = strsep(&line, ",");
6361eaf0ac3Slogwang 		} while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0');
6371eaf0ac3Slogwang 	} while (gr->gr_mem[ndx++] != NULL);
6381eaf0ac3Slogwang 
6391eaf0ac3Slogwang 	return (true);
6401eaf0ac3Slogwang }
6411eaf0ac3Slogwang 
6421eaf0ac3Slogwang /*
6431eaf0ac3Slogwang  * Create a struct group from a line.
6441eaf0ac3Slogwang  */
6451eaf0ac3Slogwang struct group *
gr_scan(const char * line)6461eaf0ac3Slogwang gr_scan(const char *line)
6471eaf0ac3Slogwang {
6481eaf0ac3Slogwang 	struct group gr;
6491eaf0ac3Slogwang 	char *line_copy;
6501eaf0ac3Slogwang 	struct group *new_gr;
6511eaf0ac3Slogwang 
6521eaf0ac3Slogwang 	if ((line_copy = strdup(line)) == NULL)
6531eaf0ac3Slogwang 		return (NULL);
6541eaf0ac3Slogwang 	if (!__gr_scan(line_copy, &gr)) {
6551eaf0ac3Slogwang 		free(line_copy);
6561eaf0ac3Slogwang 		return (NULL);
6571eaf0ac3Slogwang 	}
6581eaf0ac3Slogwang 	new_gr = gr_dup(&gr);
6591eaf0ac3Slogwang 	free(line_copy);
6601eaf0ac3Slogwang 	if (gr.gr_mem != NULL)
6611eaf0ac3Slogwang 		free(gr.gr_mem);
6621eaf0ac3Slogwang 
6631eaf0ac3Slogwang 	return (new_gr);
6641eaf0ac3Slogwang }
665