gr_util.c revision 310173
1209523Srpaulo/*- 2209523Srpaulo * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org> 3209523Srpaulo * All rights reserved. 4209523Srpaulo * 5209523Srpaulo * Redistribution and use in source and binary forms, with or without 6209523Srpaulo * modification, are permitted provided that the following conditions 7209523Srpaulo * are met: 8209523Srpaulo * 1. Redistributions of source code must retain the above copyright 9209523Srpaulo * notice, this list of conditions and the following disclaimer, 10209523Srpaulo * without modification, immediately at the beginning of the file. 11209523Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 12209523Srpaulo * notice, this list of conditions and the following disclaimer in the 13209523Srpaulo * documentation and/or other materials provided with the distribution. 14209523Srpaulo * 15209523Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16209523Srpaulo * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17209523Srpaulo * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18209523Srpaulo * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19209523Srpaulo * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20209523Srpaulo * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21209523Srpaulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22209523Srpaulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23209523Srpaulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24209523Srpaulo * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25209523Srpaulo */ 26209523Srpaulo 27215010Sjhb#include <sys/cdefs.h> 28209523Srpaulo__FBSDID("$FreeBSD: stable/10/lib/libutil/gr_util.c 310173 2016-12-16 20:10:55Z asomers $"); 29209523Srpaulo 30209523Srpaulo#include <sys/param.h> 31209523Srpaulo#include <sys/errno.h> 32209523Srpaulo#include <sys/stat.h> 33209523Srpaulo 34215010Sjhb#include <ctype.h> 35209523Srpaulo#include <err.h> 36209523Srpaulo#include <fcntl.h> 37209523Srpaulo#include <grp.h> 38209523Srpaulo#include <inttypes.h> 39209523Srpaulo#include <libutil.h> 40209523Srpaulo#include <paths.h> 41209523Srpaulo#include <stdbool.h> 42209523Srpaulo#include <stdio.h> 43209523Srpaulo#include <stdlib.h> 44209523Srpaulo#include <string.h> 45209523Srpaulo#include <unistd.h> 46209523Srpaulo 47209523Srpaulostatic int lockfd = -1; 48209523Srpaulostatic char group_dir[PATH_MAX]; 49209523Srpaulostatic char group_file[PATH_MAX]; 50209523Srpaulostatic char tempname[PATH_MAX]; 51209523Srpaulostatic int initialized; 52209523Srpaulostatic size_t grmemlen(const struct group *, const char *, int *); 53209523Srpaulostatic struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx); 54209523Srpaulo 55209523Srpaulo/* 56209523Srpaulo * Initialize statics 57209523Srpaulo */ 58209523Srpauloint 59209523Srpaulogr_init(const char *dir, const char *group) 60209523Srpaulo{ 61209523Srpaulo 62209523Srpaulo if (dir == NULL) { 63209523Srpaulo strcpy(group_dir, _PATH_ETC); 64209523Srpaulo } else { 65209523Srpaulo if (strlen(dir) >= sizeof(group_dir)) { 66209523Srpaulo errno = ENAMETOOLONG; 67209523Srpaulo return (-1); 68209523Srpaulo } 69209523Srpaulo strcpy(group_dir, dir); 70209523Srpaulo } 71209523Srpaulo 72209523Srpaulo if (group == NULL) { 73209523Srpaulo if (dir == NULL) { 74209523Srpaulo strcpy(group_file, _PATH_GROUP); 75209523Srpaulo } else if (snprintf(group_file, sizeof(group_file), "%s/group", 76209523Srpaulo group_dir) > (int)sizeof(group_file)) { 77209523Srpaulo errno = ENAMETOOLONG; 78209523Srpaulo return (-1); 79209523Srpaulo } 80209523Srpaulo } else { 81209523Srpaulo if (strlen(group) >= sizeof(group_file)) { 82209523Srpaulo errno = ENAMETOOLONG; 83209523Srpaulo return (-1); 84209523Srpaulo } 85209523Srpaulo strcpy(group_file, group); 86209523Srpaulo } 87209523Srpaulo 88209523Srpaulo initialized = 1; 89209523Srpaulo return (0); 90209523Srpaulo} 91209523Srpaulo 92209523Srpaulo/* 93233648Seadler * Lock the group file 94209523Srpaulo */ 95209523Srpauloint 96209523Srpaulogr_lock(void) 97209523Srpaulo{ 98233648Seadler if (*group_file == '\0') 99209523Srpaulo return (-1); 100209523Srpaulo 101233648Seadler for (;;) { 102209523Srpaulo struct stat st; 103209523Srpaulo 104209523Srpaulo lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); 105209523Srpaulo if (lockfd == -1) { 106209523Srpaulo if (errno == EWOULDBLOCK) { 107209523Srpaulo errx(1, "the group file is busy"); 108209523Srpaulo } else { 109209523Srpaulo err(1, "could not lock the group file: "); 110209523Srpaulo } 111209523Srpaulo } 112209523Srpaulo if (fstat(lockfd, &st) == -1) 113209523Srpaulo err(1, "fstat() failed: "); 114209523Srpaulo if (st.st_nlink != 0) 115209523Srpaulo break; 116209523Srpaulo close(lockfd); 117209523Srpaulo lockfd = -1; 118209523Srpaulo } 119209523Srpaulo return (lockfd); 120209523Srpaulo} 121209523Srpaulo 122209523Srpaulo/* 123209523Srpaulo * Create and open a presmuably safe temp file for editing group data 124209523Srpaulo */ 125209523Srpauloint 126209523Srpaulogr_tmp(int mfd) 127209523Srpaulo{ 128209523Srpaulo char buf[8192]; 129209523Srpaulo ssize_t nr; 130209523Srpaulo const char *p; 131209523Srpaulo int tfd; 132209523Srpaulo 133209523Srpaulo if (*group_file == '\0') 134209523Srpaulo return (-1); 135209523Srpaulo if ((p = strrchr(group_file, '/'))) 136209523Srpaulo ++p; 137209523Srpaulo else 138209523Srpaulo p = group_file; 139209523Srpaulo if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", 140209523Srpaulo (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { 141209523Srpaulo errno = ENAMETOOLONG; 142209523Srpaulo return (-1); 143209523Srpaulo } 144209523Srpaulo if ((tfd = mkostemp(tempname, 0)) == -1) 145209523Srpaulo return (-1); 146209523Srpaulo if (mfd != -1) { 147209523Srpaulo while ((nr = read(mfd, buf, sizeof(buf))) > 0) 148209523Srpaulo if (write(tfd, buf, (size_t)nr) != nr) 149209523Srpaulo break; 150209523Srpaulo if (nr != 0) { 151209523Srpaulo unlink(tempname); 152209523Srpaulo *tempname = '\0'; 153209523Srpaulo close(tfd); 154209523Srpaulo return (-1); 155209523Srpaulo } 156209523Srpaulo } 157209523Srpaulo return (tfd); 158209523Srpaulo} 159209523Srpaulo 160209523Srpaulo/* 161209523Srpaulo * Copy the group file from one descriptor to another, replacing, deleting 162209523Srpaulo * or adding a single record on the way. 163209523Srpaulo */ 164209523Srpauloint 165209523Srpaulogr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) 166209523Srpaulo{ 167209523Srpaulo char buf[8192], *end, *line, *p, *q, *r, t; 168209523Srpaulo struct group *fgr; 169209523Srpaulo const struct group *sgr; 170209523Srpaulo size_t len; 171209523Srpaulo int eof, readlen; 172209523Srpaulo 173209523Srpaulo if (old_gr == NULL && gr == NULL) 174209523Srpaulo return(-1); 175209523Srpaulo 176209523Srpaulo sgr = old_gr; 177209523Srpaulo /* deleting a group */ 178209523Srpaulo if (gr == NULL) { 179209523Srpaulo line = NULL; 180209523Srpaulo } else { 181209523Srpaulo if ((line = gr_make(gr)) == NULL) 182209523Srpaulo return (-1); 183209523Srpaulo } 184209523Srpaulo 185209523Srpaulo /* adding a group */ 186209523Srpaulo if (sgr == NULL) 187209523Srpaulo sgr = gr; 188209523Srpaulo 189209523Srpaulo eof = 0; 190209523Srpaulo len = 0; 191209523Srpaulo p = q = end = buf; 192209523Srpaulo for (;;) { 193209523Srpaulo /* find the end of the current line */ 194209523Srpaulo for (p = q; q < end && *q != '\0'; ++q) 195209523Srpaulo if (*q == '\n') 196209523Srpaulo break; 197209523Srpaulo 198209523Srpaulo /* if we don't have a complete line, fill up the buffer */ 199209523Srpaulo if (q >= end) { 200209523Srpaulo if (eof) 201209523Srpaulo break; 202209523Srpaulo if ((size_t)(q - p) >= sizeof(buf)) { 203209523Srpaulo warnx("group line too long"); 204209523Srpaulo errno = EINVAL; /* hack */ 205209523Srpaulo goto err; 206209523Srpaulo } 207209523Srpaulo if (p < end) { 208 q = memmove(buf, p, end -p); 209 end -= p - buf; 210 } else { 211 p = q = end = buf; 212 } 213 readlen = read(ffd, end, sizeof(buf) - (end -buf)); 214 if (readlen == -1) 215 goto err; 216 else 217 len = (size_t)readlen; 218 if (len == 0 && p == buf) 219 break; 220 end += len; 221 len = end - buf; 222 if (len < (ssize_t)sizeof(buf)) { 223 eof = 1; 224 if (len > 0 && buf[len -1] != '\n') 225 ++len, *end++ = '\n'; 226 } 227 continue; 228 } 229 230 /* is it a blank line or a comment? */ 231 for (r = p; r < q && isspace(*r); ++r) 232 /* nothing */; 233 if (r == q || *r == '#') { 234 /* yep */ 235 if (write(tfd, p, q -p + 1) != q - p + 1) 236 goto err; 237 ++q; 238 continue; 239 } 240 241 /* is it the one we're looking for? */ 242 243 t = *q; 244 *q = '\0'; 245 246 fgr = gr_scan(r); 247 248 /* fgr is either a struct group for the current line, 249 * or NULL if the line is malformed. 250 */ 251 252 *q = t; 253 if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { 254 /* nope */ 255 if (fgr != NULL) 256 free(fgr); 257 if (write(tfd, p, q - p + 1) != q - p + 1) 258 goto err; 259 ++q; 260 continue; 261 } 262 if (old_gr && !gr_equal(fgr, old_gr)) { 263 warnx("entry inconsistent"); 264 free(fgr); 265 errno = EINVAL; /* hack */ 266 goto err; 267 } 268 free(fgr); 269 270 /* it is, replace or remove it */ 271 if (line != NULL) { 272 len = strlen(line); 273 if (write(tfd, line, len) != (int) len) 274 goto err; 275 } else { 276 /* when removed, avoid the \n */ 277 q++; 278 } 279 /* we're done, just copy the rest over */ 280 for (;;) { 281 if (write(tfd, q, end - q) != end - q) 282 goto err; 283 q = buf; 284 readlen = read(ffd, buf, sizeof(buf)); 285 if (readlen == 0) 286 break; 287 else 288 len = (size_t)readlen; 289 if (readlen == -1) 290 goto err; 291 end = buf + len; 292 } 293 goto done; 294 } 295 296 /* if we got here, we didn't find the old entry */ 297 if (line == NULL) { 298 errno = ENOENT; 299 goto err; 300 } 301 len = strlen(line); 302 if ((size_t)write(tfd, line, len) != len || 303 write(tfd, "\n", 1) != 1) 304 goto err; 305 done: 306 if (line != NULL) 307 free(line); 308 return (0); 309 err: 310 if (line != NULL) 311 free(line); 312 return (-1); 313} 314 315/* 316 * Regenerate the group file 317 */ 318int 319gr_mkdb(void) 320{ 321 int fd; 322 323 if (chmod(tempname, 0644) != 0) 324 return (-1); 325 326 if (rename(tempname, group_file) != 0) 327 return (-1); 328 329 /* 330 * Make sure new group file is safe on disk. To improve performance we 331 * will call fsync() to the directory where file lies 332 */ 333 if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1) 334 return (-1); 335 336 if (fsync(fd) != 0) { 337 close(fd); 338 return (-1); 339 } 340 341 close(fd); 342 return(0); 343} 344 345/* 346 * Clean up. Preserves errno for the caller's convenience. 347 */ 348void 349gr_fini(void) 350{ 351 int serrno; 352 353 if (!initialized) 354 return; 355 initialized = 0; 356 serrno = errno; 357 if (*tempname != '\0') { 358 unlink(tempname); 359 *tempname = '\0'; 360 } 361 if (lockfd != -1) 362 close(lockfd); 363 errno = serrno; 364} 365 366/* 367 * Compares two struct group's. 368 */ 369int 370gr_equal(const struct group *gr1, const struct group *gr2) 371{ 372 int gr1_ndx; 373 int gr2_ndx; 374 375 /* Check that the non-member information is the same. */ 376 if (gr1->gr_name == NULL || gr2->gr_name == NULL) { 377 if (gr1->gr_name != gr2->gr_name) 378 return (false); 379 } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0) 380 return (false); 381 if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) { 382 if (gr1->gr_passwd != gr2->gr_passwd) 383 return (false); 384 } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0) 385 return (false); 386 if (gr1->gr_gid != gr2->gr_gid) 387 return (false); 388 389 /* Check all members in both groups. 390 * getgrnam can return gr_mem with a pointer to NULL. 391 * gr_dup and gr_add strip out this superfluous NULL, setting 392 * gr_mem to NULL for no members. 393 */ 394 if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) { 395 int i; 396 397 for (i = 0; gr1->gr_mem[i] != NULL; i++) { 398 if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0) 399 return (false); 400 } 401 } 402 /* Count number of members in both structs */ 403 gr2_ndx = 0; 404 if (gr2->gr_mem != NULL) 405 for(; gr2->gr_mem[gr2_ndx] != NULL; gr2_ndx++) 406 /* empty */; 407 gr1_ndx = 0; 408 if (gr1->gr_mem != NULL) 409 for(; gr1->gr_mem[gr1_ndx] != NULL; gr1_ndx++) 410 /* empty */; 411 if (gr1_ndx != gr2_ndx) 412 return (false); 413 414 return (true); 415} 416 417/* 418 * Make a group line out of a struct group. 419 */ 420char * 421gr_make(const struct group *gr) 422{ 423 const char *group_line_format = "%s:%s:%ju:"; 424 const char *sep; 425 char *line; 426 char *p; 427 size_t line_size; 428 int ndx; 429 430 /* Calculate the length of the group line. */ 431 line_size = snprintf(NULL, 0, group_line_format, gr->gr_name, 432 gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; 433 if (gr->gr_mem != NULL) { 434 for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) 435 line_size += strlen(gr->gr_mem[ndx]) + 1; 436 if (ndx > 0) 437 line_size--; 438 } 439 440 /* Create the group line and fill it. */ 441 if ((line = p = malloc(line_size)) == NULL) 442 return (NULL); 443 p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd, 444 (uintmax_t)gr->gr_gid); 445 if (gr->gr_mem != NULL) { 446 sep = ""; 447 for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { 448 p = stpcpy(p, sep); 449 p = stpcpy(p, gr->gr_mem[ndx]); 450 sep = ","; 451 } 452 } 453 454 return (line); 455} 456 457/* 458 * Duplicate a struct group. 459 */ 460struct group * 461gr_dup(const struct group *gr) 462{ 463 return (gr_add(gr, NULL)); 464} 465/* 466 * Add a new member name to a struct group. 467 */ 468struct group * 469gr_add(const struct group *gr, const char *newmember) 470{ 471 char *mem; 472 size_t len; 473 int num_mem; 474 475 num_mem = 0; 476 len = grmemlen(gr, newmember, &num_mem); 477 /* Create new group and copy old group into it. */ 478 if ((mem = malloc(len)) == NULL) 479 return (NULL); 480 return (grcopy(gr, mem, newmember, num_mem)); 481} 482 483/* It is safer to walk the pointers given at gr_mem since there is no 484 * guarantee the gr_mem + strings are contiguous in the given struct group 485 * but compactify the new group into the following form. 486 * 487 * The new struct is laid out like this in memory. The example given is 488 * for a group with two members only. 489 * 490 * { 491 * (char *name) 492 * (char *passwd) 493 * (int gid) 494 * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area 495 * gr_mem area 496 * (member1 *) 497 * (member2 *) 498 * (NULL) 499 * (name string) 500 * (passwd string) 501 * (member1 string) 502 * (member2 string) 503 * } 504 */ 505/* 506 * Copy the contents of a group plus given name to a preallocated group struct 507 */ 508static struct group * 509grcopy(const struct group *gr, char *dst, const char *name, int ndx) 510{ 511 int i; 512 struct group *newgr; 513 514 newgr = (struct group *)(void *)dst; /* avoid alignment warning */ 515 dst += sizeof(*newgr); 516 if (ndx != 0) { 517 newgr->gr_mem = (char **)(void *)(dst); /* avoid alignment warning */ 518 dst += (ndx + 1) * sizeof(*newgr->gr_mem); 519 } else 520 newgr->gr_mem = NULL; 521 if (gr->gr_name != NULL) { 522 newgr->gr_name = dst; 523 dst = stpcpy(dst, gr->gr_name) + 1; 524 } else 525 newgr->gr_name = NULL; 526 if (gr->gr_passwd != NULL) { 527 newgr->gr_passwd = dst; 528 dst = stpcpy(dst, gr->gr_passwd) + 1; 529 } else 530 newgr->gr_passwd = NULL; 531 newgr->gr_gid = gr->gr_gid; 532 i = 0; 533 /* Original group struct might have a NULL gr_mem */ 534 if (gr->gr_mem != NULL) { 535 for (; gr->gr_mem[i] != NULL; i++) { 536 newgr->gr_mem[i] = dst; 537 dst = stpcpy(dst, gr->gr_mem[i]) + 1; 538 } 539 } 540 /* If name is not NULL, newgr->gr_mem is known to be not NULL */ 541 if (name != NULL) { 542 newgr->gr_mem[i++] = dst; 543 dst = stpcpy(dst, name) + 1; 544 } 545 /* if newgr->gr_mem is not NULL add NULL marker */ 546 if (newgr->gr_mem != NULL) 547 newgr->gr_mem[i] = NULL; 548 549 return (newgr); 550} 551 552/* 553 * Calculate length of a struct group + given name 554 */ 555static size_t 556grmemlen(const struct group *gr, const char *name, int *num_mem) 557{ 558 size_t len; 559 int i; 560 561 if (gr == NULL) 562 return (0); 563 /* Calculate size of the group. */ 564 len = sizeof(*gr); 565 if (gr->gr_name != NULL) 566 len += strlen(gr->gr_name) + 1; 567 if (gr->gr_passwd != NULL) 568 len += strlen(gr->gr_passwd) + 1; 569 i = 0; 570 if (gr->gr_mem != NULL) { 571 for (; gr->gr_mem[i] != NULL; i++) { 572 len += strlen(gr->gr_mem[i]) + 1; 573 len += sizeof(*gr->gr_mem); 574 } 575 } 576 if (name != NULL) { 577 i++; 578 len += strlen(name) + 1; 579 len += sizeof(*gr->gr_mem); 580 } 581 /* Allow for NULL pointer */ 582 if (i != 0) 583 len += sizeof(*gr->gr_mem); 584 *num_mem = i; 585 return(len); 586} 587 588/* 589 * Scan a line and place it into a group structure. 590 */ 591static bool 592__gr_scan(char *line, struct group *gr) 593{ 594 char *loc; 595 int ndx; 596 597 /* Assign non-member information to structure. */ 598 gr->gr_name = line; 599 if ((loc = strchr(line, ':')) == NULL) 600 return (false); 601 *loc = '\0'; 602 gr->gr_passwd = loc + 1; 603 if (*gr->gr_passwd == ':') 604 *gr->gr_passwd = '\0'; 605 else { 606 if ((loc = strchr(loc + 1, ':')) == NULL) 607 return (false); 608 *loc = '\0'; 609 } 610 if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1) 611 return (false); 612 613 /* Assign member information to structure. */ 614 if ((loc = strchr(loc + 1, ':')) == NULL) 615 return (false); 616 line = loc + 1; 617 gr->gr_mem = NULL; 618 ndx = 0; 619 do { 620 gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) * 621 (ndx + 1)); 622 if (gr->gr_mem == NULL) 623 return (false); 624 625 /* Skip locations without members (i.e., empty string). */ 626 do { 627 gr->gr_mem[ndx] = strsep(&line, ","); 628 } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0'); 629 } while (gr->gr_mem[ndx++] != NULL); 630 631 return (true); 632} 633 634/* 635 * Create a struct group from a line. 636 */ 637struct group * 638gr_scan(const char *line) 639{ 640 struct group gr; 641 char *line_copy; 642 struct group *new_gr; 643 644 if ((line_copy = strdup(line)) == NULL) 645 return (NULL); 646 if (!__gr_scan(line_copy, &gr)) { 647 free(line_copy); 648 return (NULL); 649 } 650 new_gr = gr_dup(&gr); 651 free(line_copy); 652 if (gr.gr_mem != NULL) 653 free(gr.gr_mem); 654 655 return (new_gr); 656} 657