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