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