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