X-Git-Url: https://git.cameronkatri.com/pw-darwin.git/blobdiff_plain/de100da053802312c0bbcd80656737e7ad38e106..fcf4b2f12ab5618542cc036a40239eedd658f95a:/libutil/gr_util.c diff --git a/libutil/gr_util.c b/libutil/gr_util.c index 0173595..bab4143 100644 --- a/libutil/gr_util.c +++ b/libutil/gr_util.c @@ -1,4 +1,6 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2008 Sean C. Farley * All rights reserved. * @@ -44,18 +46,13 @@ __FBSDID("$FreeBSD$"); #include #include -struct group_storage { - struct group gr; - char *members[]; -}; - static int lockfd = -1; static char group_dir[PATH_MAX]; static char group_file[PATH_MAX]; static char tempname[PATH_MAX]; static int initialized; - -static const char group_line_format[] = "%s:%s:%ju:"; +static size_t grmemlen(const struct group *, const char *, int *); +static struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx); /* * Initialize statics @@ -63,6 +60,7 @@ static const char group_line_format[] = "%s:%s:%ju:"; int gr_init(const char *dir, const char *group) { + if (dir == NULL) { strcpy(group_dir, _PATH_ETC); } else { @@ -88,6 +86,7 @@ gr_init(const char *dir, const char *group) } strcpy(group_file, group); } + initialized = 1; return (0); } @@ -104,18 +103,16 @@ gr_lock(void) for (;;) { struct stat st; - lockfd = open(group_file, O_RDONLY, 0); - if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) - err(1, "%s", group_file); - if (flock(lockfd, LOCK_EX|LOCK_NB) == -1) { + lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); + if (lockfd == -1) { if (errno == EWOULDBLOCK) { errx(1, "the group file is busy"); } else { - err(1, "could not lock the group file: "); + err(1, "could not lock the group file"); } } if (fstat(lockfd, &st) == -1) - err(1, "fstat() failed: "); + err(1, "fstat() failed"); if (st.st_nlink != 0) break; close(lockfd); @@ -146,7 +143,7 @@ gr_tmp(int mfd) errno = ENAMETOOLONG; return (-1); } - if ((tfd = mkstemp(tempname)) == -1) + if ((tfd = mkostemp(tempname, 0)) == -1) return (-1); if (mfd != -1) { while ((nr = read(mfd, buf, sizeof(buf))) > 0) @@ -169,20 +166,32 @@ gr_tmp(int mfd) int gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) { - char buf[8192], *end, *line, *p, *q, *r, t; + char *buf, *end, *line, *p, *q, *r, *tmp; struct group *fgr; const struct group *sgr; - size_t len; + size_t len, size; int eof, readlen; + char t; + + if (old_gr == NULL && gr == NULL) + return(-1); - sgr = gr; + sgr = old_gr; + /* deleting a group */ if (gr == NULL) { line = NULL; - if (old_gr == NULL) + } else { + if ((line = gr_make(gr)) == NULL) return (-1); - sgr = old_gr; - } else if ((line = gr_make(gr)) == NULL) - return (-1); + } + + /* adding a group */ + if (sgr == NULL) + sgr = gr; + + /* initialize the buffer */ + if ((buf = malloc(size = 1024)) == NULL) + goto err; eof = 0; len = 0; @@ -197,10 +206,16 @@ gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) if (q >= end) { if (eof) break; - if ((size_t)(q - p) >= sizeof(buf)) { - warnx("group line too long"); - errno = EINVAL; /* hack */ - goto err; + while ((size_t)(q - p) >= size) { + if ((tmp = reallocarray(buf, 2, size)) == NULL) { + warnx("group line too long"); + goto err; + } + p = tmp + (p - buf); + q = tmp + (q - buf); + end = tmp + (end - buf); + buf = tmp; + size = size * 2; } if (p < end) { q = memmove(buf, p, end -p); @@ -208,7 +223,7 @@ gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) } else { p = q = end = buf; } - readlen = read(ffd, end, sizeof(buf) - (end -buf)); + readlen = read(ffd, end, size - (end - buf)); if (readlen == -1) goto err; else @@ -217,7 +232,7 @@ gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) break; end += len; len = end - buf; - if (len < (ssize_t)sizeof(buf)) { + if (len < size) { eof = 1; if (len > 0 && buf[len -1] != '\n') ++len, *end++ = '\n'; @@ -279,7 +294,7 @@ gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) if (write(tfd, q, end - q) != end - q) goto err; q = buf; - readlen = read(ffd, buf, sizeof(buf)); + readlen = read(ffd, buf, size); if (readlen == 0) break; else @@ -301,12 +316,12 @@ gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) write(tfd, "\n", 1) != 1) goto err; done: - if (line != NULL) - free(line); + free(line); + free(buf); return (0); err: - if (line != NULL) - free(line); + free(line); + free(buf); return (-1); } @@ -316,11 +331,32 @@ gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) int gr_mkdb(void) { - return (rename(tempname, group_file)); + int fd; + + if (chmod(tempname, 0644) != 0) + return (-1); + + if (rename(tempname, group_file) != 0) + return (-1); + + /* + * Make sure new group file is safe on disk. To improve performance we + * will call fsync() to the directory where file lies + */ + if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1) + return (-1); + + if (fsync(fd) != 0) { + close(fd); + return (-1); + } + + close(fd); + return(0); } /* - * Clean up. Preserver errno for the caller's convenience. + * Clean up. Preserves errno for the caller's convenience. */ void gr_fini(void) @@ -346,9 +382,6 @@ gr_fini(void) int gr_equal(const struct group *gr1, const struct group *gr2) { - int gr1_ndx; - int gr2_ndx; - bool found; /* Check that the non-member information is the same. */ if (gr1->gr_name == NULL || gr2->gr_name == NULL) { @@ -364,27 +397,26 @@ gr_equal(const struct group *gr1, const struct group *gr2) if (gr1->gr_gid != gr2->gr_gid) return (false); - /* Check all members in both groups. */ - if (gr1->gr_mem == NULL || gr2->gr_mem == NULL) { - if (gr1->gr_mem != gr2->gr_mem) - return (false); - } else { - for (found = false, gr1_ndx = 0; gr1->gr_mem[gr1_ndx] != NULL; - gr1_ndx++) { - for (gr2_ndx = 0; gr2->gr_mem[gr2_ndx] != NULL; - gr2_ndx++) - if (strcmp(gr1->gr_mem[gr1_ndx], - gr2->gr_mem[gr2_ndx]) == 0) { - found = true; - break; - } - if (!found) + /* + * Check all members in both groups. + * getgrnam can return gr_mem with a pointer to NULL. + * gr_dup and gr_add strip out this superfluous NULL, setting + * gr_mem to NULL for no members. + */ + if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) { + int i; + + for (i = 0; + gr1->gr_mem[i] != NULL && gr2->gr_mem[i] != NULL; i++) { + if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0) return (false); } - - /* Check that group2 does not have more members than group1. */ - if (gr2->gr_mem[gr1_ndx] != NULL) + if (gr1->gr_mem[i] != NULL || gr2->gr_mem[i] != NULL) return (false); + } else if (gr1->gr_mem != NULL && gr1->gr_mem[0] != NULL) { + return (false); + } else if (gr2->gr_mem != NULL && gr2->gr_mem[0] != NULL) { + return (false); } return (true); @@ -396,7 +428,10 @@ gr_equal(const struct group *gr1, const struct group *gr2) char * gr_make(const struct group *gr) { + const char *group_line_format = "%s:%s:%ju:"; + const char *sep; char *line; + char *p; size_t line_size; int ndx; @@ -411,16 +446,18 @@ gr_make(const struct group *gr) } /* Create the group line and fill it. */ - if ((line = malloc(line_size)) == NULL) + if ((line = p = malloc(line_size)) == NULL) return (NULL); - snprintf(line, line_size, group_line_format, gr->gr_name, gr->gr_passwd, + p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd, (uintmax_t)gr->gr_gid); - if (gr->gr_mem != NULL) + if (gr->gr_mem != NULL) { + sep = ""; for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { - strcat(line, gr->gr_mem[ndx]); - if (gr->gr_mem[ndx + 1] != NULL) - strcat(line, ","); + p = stpcpy(p, sep); + p = stpcpy(p, gr->gr_mem[ndx]); + sep = ","; } + } return (line); } @@ -431,48 +468,129 @@ gr_make(const struct group *gr) struct group * gr_dup(const struct group *gr) { - char *dst; + return (gr_add(gr, NULL)); +} +/* + * Add a new member name to a struct group. + */ +struct group * +gr_add(const struct group *gr, const char *newmember) +{ + char *mem; size_t len; - struct group_storage *gs; - int ndx; int num_mem; - /* Calculate size of the group. */ - len = sizeof(*gs); - if (gr->gr_name != NULL) - len += strlen(gr->gr_name) + 1; - if (gr->gr_passwd != NULL) - len += strlen(gr->gr_passwd) + 1; - if (gr->gr_mem != NULL) { - for (num_mem = 0; gr->gr_mem[num_mem] != NULL; num_mem++) - len += strlen(gr->gr_mem[num_mem]) + 1; - len += (num_mem + 1) * sizeof(*gr->gr_mem); - } else - num_mem = -1; - + num_mem = 0; + len = grmemlen(gr, newmember, &num_mem); /* Create new group and copy old group into it. */ - if ((gs = calloc(1, len)) == NULL) + if ((mem = malloc(len)) == NULL) return (NULL); - dst = (char *)&gs->members[num_mem + 1]; + return (grcopy(gr, mem, newmember, num_mem)); +} + +/* It is safer to walk the pointers given at gr_mem since there is no + * guarantee the gr_mem + strings are contiguous in the given struct group + * but compactify the new group into the following form. + * + * The new struct is laid out like this in memory. The example given is + * for a group with two members only. + * + * { + * (char *name) + * (char *passwd) + * (int gid) + * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area + * gr_mem area + * (member1 *) + * (member2 *) + * (NULL) + * (name string) + * (passwd string) + * (member1 string) + * (member2 string) + * } + */ +/* + * Copy the contents of a group plus given name to a preallocated group struct + */ +static struct group * +grcopy(const struct group *gr, char *dst, const char *name, int ndx) +{ + int i; + struct group *newgr; + + newgr = (struct group *)(void *)dst; /* avoid alignment warning */ + dst += sizeof(*newgr); + if (ndx != 0) { + newgr->gr_mem = (char **)(void *)(dst); /* avoid alignment warning */ + dst += (ndx + 1) * sizeof(*newgr->gr_mem); + } else + newgr->gr_mem = NULL; if (gr->gr_name != NULL) { - gs->gr.gr_name = dst; - dst = stpcpy(gs->gr.gr_name, gr->gr_name) + 1; - } + newgr->gr_name = dst; + dst = stpcpy(dst, gr->gr_name) + 1; + } else + newgr->gr_name = NULL; if (gr->gr_passwd != NULL) { - gs->gr.gr_passwd = dst; - dst = stpcpy(gs->gr.gr_passwd, gr->gr_passwd) + 1; - } - gs->gr.gr_gid = gr->gr_gid; + newgr->gr_passwd = dst; + dst = stpcpy(dst, gr->gr_passwd) + 1; + } else + newgr->gr_passwd = NULL; + newgr->gr_gid = gr->gr_gid; + i = 0; + /* Original group struct might have a NULL gr_mem */ if (gr->gr_mem != NULL) { - gs->gr.gr_mem = gs->members; - for (ndx = 0; ndx < num_mem; ndx++) { - gs->gr.gr_mem[ndx] = dst; - dst = stpcpy(gs->gr.gr_mem[ndx], gr->gr_mem[ndx]) + 1; + for (; gr->gr_mem[i] != NULL; i++) { + newgr->gr_mem[i] = dst; + dst = stpcpy(dst, gr->gr_mem[i]) + 1; } - gs->gr.gr_mem[ndx] = NULL; } + /* If name is not NULL, newgr->gr_mem is known to be not NULL */ + if (name != NULL) { + newgr->gr_mem[i++] = dst; + dst = stpcpy(dst, name) + 1; + } + /* if newgr->gr_mem is not NULL add NULL marker */ + if (newgr->gr_mem != NULL) + newgr->gr_mem[i] = NULL; + + return (newgr); +} + +/* + * Calculate length of a struct group + given name + */ +static size_t +grmemlen(const struct group *gr, const char *name, int *num_mem) +{ + size_t len; + int i; - return (&gs->gr); + if (gr == NULL) + return (0); + /* Calculate size of the group. */ + len = sizeof(*gr); + if (gr->gr_name != NULL) + len += strlen(gr->gr_name) + 1; + if (gr->gr_passwd != NULL) + len += strlen(gr->gr_passwd) + 1; + i = 0; + if (gr->gr_mem != NULL) { + for (; gr->gr_mem[i] != NULL; i++) { + len += strlen(gr->gr_mem[i]) + 1; + len += sizeof(*gr->gr_mem); + } + } + if (name != NULL) { + i++; + len += strlen(name) + 1; + len += sizeof(*gr->gr_mem); + } + /* Allow for NULL pointer */ + if (i != 0) + len += sizeof(*gr->gr_mem); + *num_mem = i; + return(len); } /*