1 /*- 2 * Copyright (c) 2008 Sean C. Farley <scf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/errno.h> 32 #include <sys/stat.h> 33 34 #include <ctype.h> 35 #include <err.h> 36 #include <fcntl.h> 37 #include <grp.h> 38 #include <inttypes.h> 39 #include <libutil.h> 40 #include <paths.h> 41 #include <stdbool.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 static int lockfd = -1; 48 static char group_dir[PATH_MAX]; 49 static char group_file[PATH_MAX]; 50 static char tempname[PATH_MAX]; 51 static int initialized; 52 static size_t grmemlen(const struct group *, const char *, int *); 53 static struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx); 54 55 /* 56 * Initialize statics 57 */ 58 int 59 gr_init(const char *dir, const char *group) 60 { 61 62 if (dir == NULL) { 63 strcpy(group_dir, _PATH_ETC); 64 } else { 65 if (strlen(dir) >= sizeof(group_dir)) { 66 errno = ENAMETOOLONG; 67 return (-1); 68 } 69 strcpy(group_dir, dir); 70 } 71 72 if (group == NULL) { 73 if (dir == NULL) { 74 strcpy(group_file, _PATH_GROUP); 75 } else if (snprintf(group_file, sizeof(group_file), "%s/group", 76 group_dir) > (int)sizeof(group_file)) { 77 errno = ENAMETOOLONG; 78 return (-1); 79 } 80 } else { 81 if (strlen(group) >= sizeof(group_file)) { 82 errno = ENAMETOOLONG; 83 return (-1); 84 } 85 strcpy(group_file, group); 86 } 87 88 initialized = 1; 89 return (0); 90 } 91 92 /* 93 * Lock the group file 94 */ 95 int 96 gr_lock(void) 97 { 98 if (*group_file == '\0') 99 return (-1); 100 101 for (;;) { 102 struct stat st; 103 104 lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0); 105 if (lockfd == -1) { 106 if (errno == EWOULDBLOCK) { 107 errx(1, "the group file is busy"); 108 } else { 109 err(1, "could not lock the group file: "); 110 } 111 } 112 if (fstat(lockfd, &st) == -1) 113 err(1, "fstat() failed: "); 114 if (st.st_nlink != 0) 115 break; 116 close(lockfd); 117 lockfd = -1; 118 } 119 return (lockfd); 120 } 121 122 /* 123 * Create and open a presmuably safe temp file for editing group data 124 */ 125 int 126 gr_tmp(int mfd) 127 { 128 char buf[8192]; 129 ssize_t nr; 130 const char *p; 131 int tfd; 132 133 if (*group_file == '\0') 134 return (-1); 135 if ((p = strrchr(group_file, '/'))) 136 ++p; 137 else 138 p = group_file; 139 if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX", 140 (int)(p - group_file), group_file) >= (int)sizeof(tempname)) { 141 errno = ENAMETOOLONG; 142 return (-1); 143 } 144 if ((tfd = mkostemp(tempname, 0)) == -1) 145 return (-1); 146 if (mfd != -1) { 147 while ((nr = read(mfd, buf, sizeof(buf))) > 0) 148 if (write(tfd, buf, (size_t)nr) != nr) 149 break; 150 if (nr != 0) { 151 unlink(tempname); 152 *tempname = '\0'; 153 close(tfd); 154 return (-1); 155 } 156 } 157 return (tfd); 158 } 159 160 /* 161 * Copy the group file from one descriptor to another, replacing, deleting 162 * or adding a single record on the way. 163 */ 164 int 165 gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr) 166 { 167 char *buf, *end, *line, *p, *q, *r, *tmp; 168 struct group *fgr; 169 const struct group *sgr; 170 size_t len, size; 171 int eof, readlen; 172 char t; 173 174 if (old_gr == NULL && gr == NULL) 175 return(-1); 176 177 sgr = old_gr; 178 /* deleting a group */ 179 if (gr == NULL) { 180 line = NULL; 181 } else { 182 if ((line = gr_make(gr)) == NULL) 183 return (-1); 184 } 185 186 /* adding a group */ 187 if (sgr == NULL) 188 sgr = gr; 189 190 /* initialize the buffer */ 191 if ((buf = malloc(size = 1024)) == NULL) 192 goto err; 193 194 eof = 0; 195 len = 0; 196 p = q = end = buf; 197 for (;;) { 198 /* find the end of the current line */ 199 for (p = q; q < end && *q != '\0'; ++q) 200 if (*q == '\n') 201 break; 202 203 /* if we don't have a complete line, fill up the buffer */ 204 if (q >= end) { 205 if (eof) 206 break; 207 while ((size_t)(q - p) >= size) { 208 if ((tmp = reallocarray(buf, 2, size)) == NULL) { 209 warnx("group line too long"); 210 goto err; 211 } 212 p = tmp + (p - buf); 213 q = tmp + (q - buf); 214 end = tmp + (end - buf); 215 buf = tmp; 216 size = size * 2; 217 } 218 if (p < end) { 219 q = memmove(buf, p, end -p); 220 end -= p - buf; 221 } else { 222 p = q = end = buf; 223 } 224 readlen = read(ffd, end, size - (end - buf)); 225 if (readlen == -1) 226 goto err; 227 else 228 len = (size_t)readlen; 229 if (len == 0 && p == buf) 230 break; 231 end += len; 232 len = end - buf; 233 if (len < size) { 234 eof = 1; 235 if (len > 0 && buf[len -1] != '\n') 236 ++len, *end++ = '\n'; 237 } 238 continue; 239 } 240 241 /* is it a blank line or a comment? */ 242 for (r = p; r < q && isspace(*r); ++r) 243 /* nothing */; 244 if (r == q || *r == '#') { 245 /* yep */ 246 if (write(tfd, p, q -p + 1) != q - p + 1) 247 goto err; 248 ++q; 249 continue; 250 } 251 252 /* is it the one we're looking for? */ 253 254 t = *q; 255 *q = '\0'; 256 257 fgr = gr_scan(r); 258 259 /* fgr is either a struct group for the current line, 260 * or NULL if the line is malformed. 261 */ 262 263 *q = t; 264 if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) { 265 /* nope */ 266 if (fgr != NULL) 267 free(fgr); 268 if (write(tfd, p, q - p + 1) != q - p + 1) 269 goto err; 270 ++q; 271 continue; 272 } 273 if (old_gr && !gr_equal(fgr, old_gr)) { 274 warnx("entry inconsistent"); 275 free(fgr); 276 errno = EINVAL; /* hack */ 277 goto err; 278 } 279 free(fgr); 280 281 /* it is, replace or remove it */ 282 if (line != NULL) { 283 len = strlen(line); 284 if (write(tfd, line, len) != (int) len) 285 goto err; 286 } else { 287 /* when removed, avoid the \n */ 288 q++; 289 } 290 /* we're done, just copy the rest over */ 291 for (;;) { 292 if (write(tfd, q, end - q) != end - q) 293 goto err; 294 q = buf; 295 readlen = read(ffd, buf, size); 296 if (readlen == 0) 297 break; 298 else 299 len = (size_t)readlen; 300 if (readlen == -1) 301 goto err; 302 end = buf + len; 303 } 304 goto done; 305 } 306 307 /* if we got here, we didn't find the old entry */ 308 if (line == NULL) { 309 errno = ENOENT; 310 goto err; 311 } 312 len = strlen(line); 313 if ((size_t)write(tfd, line, len) != len || 314 write(tfd, "\n", 1) != 1) 315 goto err; 316 done: 317 free(line); 318 free(buf); 319 return (0); 320 err: 321 free(line); 322 free(buf); 323 return (-1); 324 } 325 326 /* 327 * Regenerate the group file 328 */ 329 int 330 gr_mkdb(void) 331 { 332 int fd; 333 334 if (chmod(tempname, 0644) != 0) 335 return (-1); 336 337 if (rename(tempname, group_file) != 0) 338 return (-1); 339 340 /* 341 * Make sure new group file is safe on disk. To improve performance we 342 * will call fsync() to the directory where file lies 343 */ 344 if ((fd = open(group_dir, O_RDONLY|O_DIRECTORY)) == -1) 345 return (-1); 346 347 if (fsync(fd) != 0) { 348 close(fd); 349 return (-1); 350 } 351 352 close(fd); 353 return(0); 354 } 355 356 /* 357 * Clean up. Preserves errno for the caller's convenience. 358 */ 359 void 360 gr_fini(void) 361 { 362 int serrno; 363 364 if (!initialized) 365 return; 366 initialized = 0; 367 serrno = errno; 368 if (*tempname != '\0') { 369 unlink(tempname); 370 *tempname = '\0'; 371 } 372 if (lockfd != -1) 373 close(lockfd); 374 errno = serrno; 375 } 376 377 /* 378 * Compares two struct group's. 379 */ 380 int 381 gr_equal(const struct group *gr1, const struct group *gr2) 382 { 383 384 /* Check that the non-member information is the same. */ 385 if (gr1->gr_name == NULL || gr2->gr_name == NULL) { 386 if (gr1->gr_name != gr2->gr_name) 387 return (false); 388 } else if (strcmp(gr1->gr_name, gr2->gr_name) != 0) 389 return (false); 390 if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) { 391 if (gr1->gr_passwd != gr2->gr_passwd) 392 return (false); 393 } else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0) 394 return (false); 395 if (gr1->gr_gid != gr2->gr_gid) 396 return (false); 397 398 /* 399 * Check all members in both groups. 400 * getgrnam can return gr_mem with a pointer to NULL. 401 * gr_dup and gr_add strip out this superfluous NULL, setting 402 * gr_mem to NULL for no members. 403 */ 404 if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) { 405 int i; 406 407 for (i = 0; 408 gr1->gr_mem[i] != NULL && gr2->gr_mem[i] != NULL; i++) { 409 if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0) 410 return (false); 411 } 412 if (gr1->gr_mem[i] != NULL || gr2->gr_mem[i] != NULL) 413 return (false); 414 } else if (gr1->gr_mem != NULL && gr1->gr_mem[0] != NULL) { 415 return (false); 416 } else if (gr2->gr_mem != NULL && gr2->gr_mem[0] != NULL) { 417 return (false); 418 } 419 420 return (true); 421 } 422 423 /* 424 * Make a group line out of a struct group. 425 */ 426 char * 427 gr_make(const struct group *gr) 428 { 429 const char *group_line_format = "%s:%s:%ju:"; 430 const char *sep; 431 char *line; 432 char *p; 433 size_t line_size; 434 int ndx; 435 436 /* Calculate the length of the group line. */ 437 line_size = snprintf(NULL, 0, group_line_format, gr->gr_name, 438 gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1; 439 if (gr->gr_mem != NULL) { 440 for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) 441 line_size += strlen(gr->gr_mem[ndx]) + 1; 442 if (ndx > 0) 443 line_size--; 444 } 445 446 /* Create the group line and fill it. */ 447 if ((line = p = malloc(line_size)) == NULL) 448 return (NULL); 449 p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd, 450 (uintmax_t)gr->gr_gid); 451 if (gr->gr_mem != NULL) { 452 sep = ""; 453 for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) { 454 p = stpcpy(p, sep); 455 p = stpcpy(p, gr->gr_mem[ndx]); 456 sep = ","; 457 } 458 } 459 460 return (line); 461 } 462 463 /* 464 * Duplicate a struct group. 465 */ 466 struct group * 467 gr_dup(const struct group *gr) 468 { 469 return (gr_add(gr, NULL)); 470 } 471 /* 472 * Add a new member name to a struct group. 473 */ 474 struct group * 475 gr_add(const struct group *gr, const char *newmember) 476 { 477 char *mem; 478 size_t len; 479 int num_mem; 480 481 num_mem = 0; 482 len = grmemlen(gr, newmember, &num_mem); 483 /* Create new group and copy old group into it. */ 484 if ((mem = malloc(len)) == NULL) 485 return (NULL); 486 return (grcopy(gr, mem, newmember, num_mem)); 487 } 488 489 /* It is safer to walk the pointers given at gr_mem since there is no 490 * guarantee the gr_mem + strings are contiguous in the given struct group 491 * but compactify the new group into the following form. 492 * 493 * The new struct is laid out like this in memory. The example given is 494 * for a group with two members only. 495 * 496 * { 497 * (char *name) 498 * (char *passwd) 499 * (int gid) 500 * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area 501 * gr_mem area 502 * (member1 *) 503 * (member2 *) 504 * (NULL) 505 * (name string) 506 * (passwd string) 507 * (member1 string) 508 * (member2 string) 509 * } 510 */ 511 /* 512 * Copy the contents of a group plus given name to a preallocated group struct 513 */ 514 static struct group * 515 grcopy(const struct group *gr, char *dst, const char *name, int ndx) 516 { 517 int i; 518 struct group *newgr; 519 520 newgr = (struct group *)(void *)dst; /* avoid alignment warning */ 521 dst += sizeof(*newgr); 522 if (ndx != 0) { 523 newgr->gr_mem = (char **)(void *)(dst); /* avoid alignment warning */ 524 dst += (ndx + 1) * sizeof(*newgr->gr_mem); 525 } else 526 newgr->gr_mem = NULL; 527 if (gr->gr_name != NULL) { 528 newgr->gr_name = dst; 529 dst = stpcpy(dst, gr->gr_name) + 1; 530 } else 531 newgr->gr_name = NULL; 532 if (gr->gr_passwd != NULL) { 533 newgr->gr_passwd = dst; 534 dst = stpcpy(dst, gr->gr_passwd) + 1; 535 } else 536 newgr->gr_passwd = NULL; 537 newgr->gr_gid = gr->gr_gid; 538 i = 0; 539 /* Original group struct might have a NULL gr_mem */ 540 if (gr->gr_mem != NULL) { 541 for (; gr->gr_mem[i] != NULL; i++) { 542 newgr->gr_mem[i] = dst; 543 dst = stpcpy(dst, gr->gr_mem[i]) + 1; 544 } 545 } 546 /* If name is not NULL, newgr->gr_mem is known to be not NULL */ 547 if (name != NULL) { 548 newgr->gr_mem[i++] = dst; 549 dst = stpcpy(dst, name) + 1; 550 } 551 /* if newgr->gr_mem is not NULL add NULL marker */ 552 if (newgr->gr_mem != NULL) 553 newgr->gr_mem[i] = NULL; 554 555 return (newgr); 556 } 557 558 /* 559 * Calculate length of a struct group + given name 560 */ 561 static size_t 562 grmemlen(const struct group *gr, const char *name, int *num_mem) 563 { 564 size_t len; 565 int i; 566 567 if (gr == NULL) 568 return (0); 569 /* Calculate size of the group. */ 570 len = sizeof(*gr); 571 if (gr->gr_name != NULL) 572 len += strlen(gr->gr_name) + 1; 573 if (gr->gr_passwd != NULL) 574 len += strlen(gr->gr_passwd) + 1; 575 i = 0; 576 if (gr->gr_mem != NULL) { 577 for (; gr->gr_mem[i] != NULL; i++) { 578 len += strlen(gr->gr_mem[i]) + 1; 579 len += sizeof(*gr->gr_mem); 580 } 581 } 582 if (name != NULL) { 583 i++; 584 len += strlen(name) + 1; 585 len += sizeof(*gr->gr_mem); 586 } 587 /* Allow for NULL pointer */ 588 if (i != 0) 589 len += sizeof(*gr->gr_mem); 590 *num_mem = i; 591 return(len); 592 } 593 594 /* 595 * Scan a line and place it into a group structure. 596 */ 597 static bool 598 __gr_scan(char *line, struct group *gr) 599 { 600 char *loc; 601 int ndx; 602 603 /* Assign non-member information to structure. */ 604 gr->gr_name = line; 605 if ((loc = strchr(line, ':')) == NULL) 606 return (false); 607 *loc = '\0'; 608 gr->gr_passwd = loc + 1; 609 if (*gr->gr_passwd == ':') 610 *gr->gr_passwd = '\0'; 611 else { 612 if ((loc = strchr(loc + 1, ':')) == NULL) 613 return (false); 614 *loc = '\0'; 615 } 616 if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1) 617 return (false); 618 619 /* Assign member information to structure. */ 620 if ((loc = strchr(loc + 1, ':')) == NULL) 621 return (false); 622 line = loc + 1; 623 gr->gr_mem = NULL; 624 ndx = 0; 625 do { 626 gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) * 627 (ndx + 1)); 628 if (gr->gr_mem == NULL) 629 return (false); 630 631 /* Skip locations without members (i.e., empty string). */ 632 do { 633 gr->gr_mem[ndx] = strsep(&line, ","); 634 } while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0'); 635 } while (gr->gr_mem[ndx++] != NULL); 636 637 return (true); 638 } 639 640 /* 641 * Create a struct group from a line. 642 */ 643 struct group * 644 gr_scan(const char *line) 645 { 646 struct group gr; 647 char *line_copy; 648 struct group *new_gr; 649 650 if ((line_copy = strdup(line)) == NULL) 651 return (NULL); 652 if (!__gr_scan(line_copy, &gr)) { 653 free(line_copy); 654 return (NULL); 655 } 656 new_gr = gr_dup(&gr); 657 free(line_copy); 658 if (gr.gr_mem != NULL) 659 free(gr.gr_mem); 660 661 return (new_gr); 662 } 663