1 /* $NetBSD: getcap.c,v 1.51 2012/03/13 21:13:35 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Casey Leedom of Lawrence Livermore National Laboratory. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #if HAVE_NBTOOL_CONFIG_H 36 #include "nbtool_config.h" 37 #endif 38 39 #include <sys/cdefs.h> 40 #if defined(LIBC_SCCS) && !defined(lint) 41 #if 0 42 static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; 43 #else 44 __RCSID("$NetBSD: getcap.c,v 1.51 2012/03/13 21:13:35 christos Exp $"); 45 #endif 46 #endif /* LIBC_SCCS and not lint */ 47 48 #ifndef SMALL 49 #include "namespace.h" 50 #endif 51 #include <sys/types.h> 52 #include <sys/param.h> 53 54 #include <assert.h> 55 #include <stddef.h> 56 #include <ctype.h> 57 #ifndef SMALL 58 #include <db.h> 59 #endif 60 #include <errno.h> 61 #include <fcntl.h> 62 #include <limits.h> 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <unistd.h> 67 68 #if defined(__weak_alias) && !defined(SMALL) 69 __weak_alias(cgetcap,_cgetcap) 70 __weak_alias(cgetclose,_cgetclose) 71 __weak_alias(cgetent,_cgetent) 72 __weak_alias(cgetfirst,_cgetfirst) 73 __weak_alias(cgetmatch,_cgetmatch) 74 __weak_alias(cgetnext,_cgetnext) 75 __weak_alias(cgetnum,_cgetnum) 76 __weak_alias(cgetset,_cgetset) 77 __weak_alias(cgetstr,_cgetstr) 78 __weak_alias(cgetustr,_cgetustr) 79 __weak_alias(csetexpandtc,_csetexpandtc) 80 #endif 81 82 #define BFRAG 1024 83 #define BSIZE 1024 84 #define ESC ('[' & 037) /* ASCII ESC */ 85 #define MAX_RECURSION 32 /* maximum getent recursion */ 86 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 87 88 #define RECOK (char)0 89 #define TCERR (char)1 90 #define SHADOW (char)2 91 92 static size_t topreclen; /* toprec length */ 93 static char *toprec; /* Additional record specified by cgetset() */ 94 static int gottoprec; /* Flag indicating retrieval of toprecord */ 95 static int expandtc = 1; /* flag to expand tc= or not */ 96 97 #ifndef SMALL 98 static int cdbget(DB *, char **, const char *); 99 #endif 100 static int getent(char **, size_t *, const char * const *, int, 101 const char *, int, char *); 102 static int nfcmp(char *, char *); 103 104 /* 105 * Cgetset() allows the addition of a user specified buffer to be added 106 * to the database array, in effect "pushing" the buffer on top of the 107 * virtual database. 0 is returned on success, -1 on failure. 108 */ 109 int 110 cgetset(const char *ent) 111 { 112 const char *source, *check; 113 char *dest; 114 115 if (ent == NULL) { 116 if (toprec != NULL) 117 free(toprec); 118 toprec = NULL; 119 topreclen = 0; 120 return 0; 121 } 122 topreclen = strlen(ent); 123 if ((toprec = malloc(topreclen + 1)) == NULL) { 124 errno = ENOMEM; 125 return -1; 126 } 127 gottoprec = 0; 128 129 source = ent; 130 dest = toprec; 131 while (*source != '\0') { /* Strip whitespace */ 132 *dest++ = *source++; /* Do not check first field */ 133 while (*source == ':') { 134 check = source + 1; 135 while (*check && (isspace((unsigned char)*check) || 136 (*check=='\\' && isspace((unsigned char)check[1])))) 137 ++check; 138 if (*check == ':') 139 source = check; 140 else 141 break; 142 143 } 144 } 145 *dest = 0; 146 147 return 0; 148 } 149 150 /* 151 * Cgetcap searches the capability record buf for the capability cap with 152 * type `type'. A pointer to the value of cap is returned on success, NULL 153 * if the requested capability couldn't be found. 154 * 155 * Specifying a type of ':' means that nothing should follow cap (:cap:). 156 * In this case a pointer to the terminating ':' or NUL will be returned if 157 * cap is found. 158 * 159 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 160 * return NULL. 161 */ 162 char * 163 cgetcap(buf, cap, type) 164 char *buf; 165 const char *cap; 166 int type; 167 { 168 char *bp; 169 const char *cp; 170 171 _DIAGASSERT(buf != NULL); 172 _DIAGASSERT(cap != NULL); 173 174 bp = buf; 175 for (;;) { 176 /* 177 * Skip past the current capability field - it's either the 178 * name field if this is the first time through the loop, or 179 * the remainder of a field whose name failed to match cap. 180 */ 181 for (;;) 182 if (*bp == '\0') 183 return NULL; 184 else if (*bp++ == ':') 185 break; 186 187 /* 188 * Try to match (cap, type) in buf. 189 */ 190 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 191 continue; 192 if (*cp != '\0') 193 continue; 194 if (*bp == '@') 195 return NULL; 196 if (type == ':') { 197 if (*bp != '\0' && *bp != ':') 198 continue; 199 return bp; 200 } 201 if (*bp != type) 202 continue; 203 bp++; 204 return *bp == '@' ? NULL : bp; 205 } 206 /* NOTREACHED */ 207 } 208 209 /* 210 * Cgetent extracts the capability record name from the NULL terminated file 211 * array db_array and returns a pointer to a malloc'd copy of it in buf. 212 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 213 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 214 * -1 if the requested record couldn't be found, -2 if a system error was 215 * encountered (couldn't open/read a file, etc.), and -3 if a potential 216 * reference loop is detected. 217 */ 218 /* coverity[+alloc : arg-*0] */ 219 int 220 cgetent(char **buf, const char * const *db_array, const char *name) 221 { 222 size_t dummy; 223 224 _DIAGASSERT(buf != NULL); 225 _DIAGASSERT(db_array != NULL); 226 _DIAGASSERT(name != NULL); 227 228 return getent(buf, &dummy, db_array, -1, name, 0, NULL); 229 } 230 231 void 232 csetexpandtc(int etc) 233 { 234 expandtc = etc; 235 } 236 237 /* 238 * Getent implements the functions of cgetent. If fd is non-negative, 239 * *db_array has already been opened and fd is the open file descriptor. We 240 * do this to save time and avoid using up file descriptors for tc= 241 * recursions. 242 * 243 * Getent returns the same success/failure codes as cgetent. On success, a 244 * pointer to a malloc'ed capability record with all tc= capabilities fully 245 * expanded and its length (not including trailing ASCII NUL) are left in 246 * *cap and *len. 247 * 248 * Basic algorithm: 249 * + Allocate memory incrementally as needed in chunks of size BFRAG 250 * for capability buffer. 251 * + Recurse for each tc=name and interpolate result. Stop when all 252 * names interpolated, a name can't be found, or depth exceeds 253 * MAX_RECURSION. 254 */ 255 /* coverity[+alloc : arg-*0] */ 256 static int 257 getent(char **cap, size_t *len, const char * const *db_array, int fd, 258 const char *name, int depth, char *nfield) 259 { 260 #ifndef SMALL 261 DB *capdbp; 262 char pbuf[MAXPATHLEN]; 263 char *cbuf; 264 int retval; 265 size_t clen; 266 #endif 267 char *record, *newrecord; 268 char *r_end, *rp; /* pacify gcc */ 269 const char * const *db_p; 270 int myfd, eof, foundit; 271 int tc_not_resolved; 272 273 _DIAGASSERT(cap != NULL); 274 _DIAGASSERT(len != NULL); 275 _DIAGASSERT(db_array != NULL); 276 /* fd may be -1 */ 277 _DIAGASSERT(name != NULL); 278 /* nfield may be NULL */ 279 280 myfd = 0; 281 rp = NULL; 282 283 /* 284 * Return with ``loop detected'' error if we've recursed more than 285 * MAX_RECURSION times. 286 */ 287 if (depth > MAX_RECURSION) 288 return -3; 289 290 /* 291 * Check if we have a top record from cgetset(). 292 */ 293 if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 294 if ((record = malloc(topreclen + BFRAG)) == NULL) { 295 errno = ENOMEM; 296 return -2; 297 } 298 (void)strcpy(record, toprec); /* XXX: strcpy is safe */ 299 db_p = db_array; 300 rp = record + topreclen + 1; 301 r_end = rp + BFRAG; 302 goto tc_exp; 303 } 304 /* 305 * Allocate first chunk of memory. 306 */ 307 if ((record = malloc(BFRAG)) == NULL) { 308 errno = ENOMEM; 309 return -2; 310 } 311 r_end = record + BFRAG; 312 foundit = 0; 313 /* 314 * Loop through database array until finding the record. 315 */ 316 317 for (db_p = db_array; *db_p != NULL; db_p++) { 318 eof = 0; 319 320 /* 321 * Open database if not already open. 322 */ 323 324 if (fd >= 0) { 325 (void)lseek(fd, (off_t)0, SEEK_SET); 326 } else { 327 #ifndef SMALL 328 (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 329 if (expandtc && 330 (capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 331 != NULL) { 332 free(record); 333 retval = cdbget(capdbp, &record, name); 334 if (retval < 0) { 335 /* no record available */ 336 (void)capdbp->close(capdbp); 337 return retval; 338 } 339 /* save the data; close frees it */ 340 clen = strlen(record); 341 if ((cbuf = malloc(clen + 1)) == NULL) { 342 (void)capdbp->close(capdbp); 343 errno = ENOMEM; 344 return -2; 345 } 346 memmove(cbuf, record, clen + 1); 347 if (capdbp->close(capdbp) < 0) { 348 int serrno = errno; 349 350 free(cbuf); 351 errno = serrno; 352 return -2; 353 } 354 *len = clen; 355 *cap = cbuf; 356 return retval; 357 } else 358 #endif 359 { 360 fd = open(*db_p, O_RDONLY, 0); 361 if (fd < 0) { 362 /* No error on unfound file. */ 363 continue; 364 } 365 myfd = 1; 366 } 367 } 368 /* 369 * Find the requested capability record ... 370 */ 371 { 372 char buf[BUFSIZ]; 373 char *b_end, *bp, *cp; 374 int c, slash; 375 376 /* 377 * Loop invariants: 378 * There is always room for one more character in record. 379 * R_end always points just past end of record. 380 * Rp always points just past last character in record. 381 * B_end always points just past last character in buf. 382 * Bp always points at next character in buf. 383 * Cp remembers where the last colon was. 384 */ 385 b_end = buf; 386 bp = buf; 387 cp = NULL; 388 slash = 0; 389 for (;;) { 390 /* 391 * Read in a line implementing (\, newline) 392 * line continuation. 393 */ 394 rp = record; 395 for (;;) { 396 if (bp >= b_end) { 397 ssize_t n; 398 399 n = read(fd, buf, sizeof(buf)); 400 if (n <= 0) { 401 if (myfd) 402 (void)close(fd); 403 if (n < 0) { 404 int serrno = errno; 405 406 free(record); 407 errno = serrno; 408 return -2; 409 } else { 410 fd = -1; 411 eof = 1; 412 break; 413 } 414 } 415 b_end = buf+n; 416 bp = buf; 417 } 418 419 c = *bp++; 420 if (c == '\n') { 421 if (slash) { 422 slash = 0; 423 rp--; 424 continue; 425 } else 426 break; 427 } 428 if (slash) { 429 slash = 0; 430 cp = 0; 431 } 432 if (c == ':') { 433 /* 434 * If the field was `empty' (i.e. 435 * contained only white space), back up 436 * to the colon (eliminating the 437 * field). 438 */ 439 if (cp != NULL) 440 rp = cp; 441 else 442 cp = rp; 443 } else if (c == '\\') { 444 slash = 1; 445 } else if (c != ' ' && c != '\t') { 446 /* 447 * Forget where the colon was, as this 448 * is not an empty field. 449 */ 450 cp = 0; 451 } 452 *rp++ = c; 453 454 /* 455 * Enforce loop invariant: if no room 456 * left in record buffer, try to get 457 * some more. 458 */ 459 if (rp >= r_end) { 460 ptrdiff_t pos; 461 size_t newsize; 462 463 pos = rp - record; 464 newsize = r_end - record + BFRAG; 465 newrecord = realloc(record, newsize); 466 if (newrecord == NULL) { 467 free(record); 468 if (myfd) 469 (void)close(fd); 470 errno = ENOMEM; 471 return -2; 472 } 473 record = newrecord; 474 r_end = record + newsize; 475 rp = record + pos; 476 } 477 } 478 /* Eliminate any white space after the last colon. */ 479 if (cp) 480 rp = cp + 1; 481 /* Loop invariant lets us do this. */ 482 *rp++ = '\0'; 483 484 /* 485 * If encountered eof check next file. 486 */ 487 if (eof) 488 break; 489 490 /* 491 * Toss blank lines and comments. 492 */ 493 if (*record == '\0' || *record == '#') 494 continue; 495 496 /* 497 * See if this is the record we want ... 498 */ 499 if (cgetmatch(record, name) == 0) 500 if (nfield == NULL || !nfcmp(nfield, record)) { 501 foundit = 1; 502 break; /* found it! */ 503 } 504 } 505 } 506 if (foundit) 507 break; 508 } 509 510 if (!foundit) 511 return -1; 512 513 /* 514 * Got the capability record, but now we have to expand all tc=name 515 * references in it ... 516 */ 517 tc_exp: 518 tc_not_resolved = 0; 519 if (expandtc) { 520 char *newicap, *s; 521 size_t ilen, newilen; 522 int iret; 523 ptrdiff_t diff, tclen; 524 char *icap, *scan, *tc, *tcstart, *tcend; 525 526 /* 527 * Loop invariants: 528 * There is room for one more character in record. 529 * R_end points just past end of record. 530 * Rp points just past last character in record. 531 * Scan points at remainder of record that needs to be 532 * scanned for tc=name constructs. 533 */ 534 scan = record; 535 for (;;) { 536 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 537 break; 538 539 /* 540 * Find end of tc=name and stomp on the trailing `:' 541 * (if present) so we can use it to call ourselves. 542 */ 543 s = tc; 544 for (;;) 545 if (*s == '\0') 546 break; 547 else 548 if (*s++ == ':') { 549 *(s - 1) = '\0'; 550 break; 551 } 552 tcstart = tc - 3; 553 tclen = s - tcstart; 554 tcend = s; 555 556 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 557 NULL); 558 newicap = icap; /* Put into a register. */ 559 newilen = ilen; 560 if (iret != 0) { 561 /* an error */ 562 if (iret < -1) { 563 if (myfd) 564 (void)close(fd); 565 free(record); 566 return iret; 567 } 568 if (iret == 1) 569 tc_not_resolved = 1; 570 /* couldn't resolve tc */ 571 if (iret == -1) { 572 *(s - 1) = ':'; 573 scan = s - 1; 574 tc_not_resolved = 1; 575 continue; 576 577 } 578 } 579 /* not interested in name field of tc'ed record */ 580 s = newicap; 581 for (;;) 582 if (*s == '\0') 583 break; 584 else if (*s++ == ':') 585 break; 586 newilen -= s - newicap; 587 newicap = s; 588 589 /* make sure interpolated record is `:'-terminated */ 590 s += newilen; 591 if (*(s - 1) != ':') { 592 *s = ':'; /* overwrite NUL with : */ 593 newilen++; 594 } 595 596 /* 597 * Make sure there's enough room to insert the 598 * new record. 599 */ 600 diff = newilen - tclen; 601 if (diff >= r_end - rp) { 602 ptrdiff_t pos, tcpos, tcposend; 603 size_t newsize; 604 605 pos = rp - record; 606 newsize = r_end - record + diff + BFRAG; 607 tcpos = tcstart - record; 608 tcposend = tcend - record; 609 newrecord = realloc(record, newsize); 610 if (newrecord == NULL) { 611 free(record); 612 if (myfd) 613 (void)close(fd); 614 free(icap); 615 errno = ENOMEM; 616 return -2; 617 } 618 record = newrecord; 619 r_end = record + newsize; 620 rp = record + pos; 621 tcstart = record + tcpos; 622 tcend = record + tcposend; 623 } 624 625 /* 626 * Insert tc'ed record into our record. 627 */ 628 s = tcstart + newilen; 629 memmove(s, tcend, (size_t)(rp - tcend)); 630 memmove(tcstart, newicap, newilen); 631 rp += diff; 632 free(icap); 633 634 /* 635 * Start scan on `:' so next cgetcap works properly 636 * (cgetcap always skips first field). 637 */ 638 scan = s - 1; 639 } 640 641 } 642 /* 643 * Close file (if we opened it), give back any extra memory, and 644 * return capability, length and success. 645 */ 646 if (myfd) 647 (void)close(fd); 648 *len = rp - record - 1; /* don't count NUL */ 649 if (r_end > rp) { 650 if ((newrecord = 651 realloc(record, (size_t)(rp - record))) == NULL) { 652 free(record); 653 errno = ENOMEM; 654 return -2; 655 } 656 record = newrecord; 657 } 658 659 *cap = record; 660 if (tc_not_resolved) 661 return 1; 662 return 0; 663 } 664 665 #ifndef SMALL 666 static int 667 cdbget(DB *capdbp, char **bp, const char *name) 668 { 669 DBT key; 670 DBT data; 671 672 _DIAGASSERT(capdbp != NULL); 673 _DIAGASSERT(bp != NULL); 674 _DIAGASSERT(name != NULL); 675 676 key.data = __UNCONST(name); 677 key.size = strlen(name); 678 679 for (;;) { 680 /* Get the reference. */ 681 switch(capdbp->get(capdbp, &key, &data, 0)) { 682 case -1: 683 return -2; 684 case 1: 685 return -1; 686 } 687 688 /* If not an index to another record, leave. */ 689 if (((char *)data.data)[0] != SHADOW) 690 break; 691 692 key.data = (char *)data.data + 1; 693 key.size = data.size - 1; 694 } 695 696 *bp = (char *)data.data + 1; 697 return ((char *)(data.data))[0] == TCERR ? 1 : 0; 698 } 699 #endif 700 701 /* 702 * Cgetmatch will return 0 if name is one of the names of the capability 703 * record buf, -1 if not. 704 */ 705 int 706 cgetmatch(const char *buf, const char *name) 707 { 708 const char *np, *bp; 709 710 _DIAGASSERT(buf != NULL); 711 _DIAGASSERT(name != NULL); 712 713 /* 714 * Start search at beginning of record. 715 */ 716 bp = buf; 717 for (;;) { 718 /* 719 * Try to match a record name. 720 */ 721 np = name; 722 for (;;) 723 if (*np == '\0') { 724 if (*bp == '|' || *bp == ':' || *bp == '\0') 725 return 0; 726 else 727 break; 728 } else if (*bp++ != *np++) 729 break; 730 731 /* 732 * Match failed, skip to next name in record. 733 */ 734 if (bp > buf) 735 bp--; /* a '|' or ':' may have stopped the match */ 736 else 737 return -1; 738 for (;;) 739 if (*bp == '\0' || *bp == ':') 740 return -1; /* match failed totally */ 741 else if (*bp++ == '|') 742 break; /* found next name */ 743 } 744 } 745 746 int 747 cgetfirst(char **buf, const char * const *db_array) 748 { 749 750 _DIAGASSERT(buf != NULL); 751 _DIAGASSERT(db_array != NULL); 752 753 (void)cgetclose(); 754 return cgetnext(buf, db_array); 755 } 756 757 static FILE *pfp; 758 static int slash; 759 static const char * const *dbp; 760 761 int 762 cgetclose(void) 763 { 764 if (pfp != NULL) { 765 (void)fclose(pfp); 766 pfp = NULL; 767 } 768 dbp = NULL; 769 gottoprec = 0; 770 slash = 0; 771 return 0; 772 } 773 774 /* 775 * Cgetnext() gets either the first or next entry in the logical database 776 * specified by db_array. It returns 0 upon completion of the database, 1 777 * upon returning an entry with more remaining, and -1 if an error occurs. 778 */ 779 /* coverity[+alloc : arg-*0] */ 780 int 781 cgetnext(char **bp, const char * const *db_array) 782 { 783 size_t len = 0; 784 int status, done; 785 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 786 size_t dummy; 787 788 _DIAGASSERT(bp != NULL); 789 _DIAGASSERT(db_array != NULL); 790 791 if (dbp == NULL) 792 dbp = db_array; 793 794 if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) { 795 (void)cgetclose(); 796 return -1; 797 } 798 for (;;) { 799 if (toprec != NULL && !gottoprec) { 800 gottoprec = 1; 801 line = toprec; 802 } else { 803 line = fgetln(pfp, &len); 804 if (line == NULL) { 805 if (pfp == NULL) 806 return -1; 807 if (ferror(pfp)) { 808 (void)cgetclose(); 809 return -1; 810 } else { 811 (void)fclose(pfp); 812 pfp = NULL; 813 if (*++dbp == NULL) { 814 (void)cgetclose(); 815 return 0; 816 } else if ((pfp = 817 fopen(*dbp, "re")) == NULL) { 818 (void)cgetclose(); 819 return -1; 820 } else 821 continue; 822 } 823 } else 824 line[len - 1] = '\0'; 825 if (len == 1) { 826 slash = 0; 827 continue; 828 } 829 if (isspace((unsigned char)*line) || 830 *line == ':' || *line == '#' || slash) { 831 if (line[len - 2] == '\\') 832 slash = 1; 833 else 834 slash = 0; 835 continue; 836 } 837 if (line[len - 2] == '\\') 838 slash = 1; 839 else 840 slash = 0; 841 } 842 843 844 /* 845 * Line points to a name line. 846 */ 847 if (len > sizeof(nbuf)) 848 return -1; 849 done = 0; 850 np = nbuf; 851 for (;;) { 852 for (cp = line; *cp != '\0'; cp++) { 853 if (*cp == ':') { 854 *np++ = ':'; 855 done = 1; 856 break; 857 } 858 if (*cp == '\\') 859 break; 860 *np++ = *cp; 861 } 862 if (done) { 863 *np = '\0'; 864 break; 865 } else { /* name field extends beyond the line */ 866 line = fgetln(pfp, &len); 867 if (line == NULL && pfp) { 868 if (ferror(pfp)) { 869 (void)cgetclose(); 870 return -1; 871 } 872 (void)fclose(pfp); 873 pfp = NULL; 874 *np = '\0'; 875 break; 876 } else 877 line[len - 1] = '\0'; 878 } 879 } 880 if (len > sizeof(buf)) 881 return -1; 882 rp = buf; 883 for (cp = nbuf; *cp != '\0'; cp++) 884 if (*cp == '|' || *cp == ':') 885 break; 886 else 887 *rp++ = *cp; 888 889 *rp = '\0'; 890 /* 891 * XXX 892 * Last argument of getent here should be nbuf if we want true 893 * sequential access in the case of duplicates. 894 * With NULL, getent will return the first entry found 895 * rather than the duplicate entry record. This is a 896 * matter of semantics that should be resolved. 897 */ 898 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 899 if (status == -2 || status == -3) 900 (void)cgetclose(); 901 902 return status + 1; 903 } 904 /* NOTREACHED */ 905 } 906 907 /* 908 * Cgetstr retrieves the value of the string capability cap from the 909 * capability record pointed to by buf. A pointer to a decoded, NUL 910 * terminated, malloc'd copy of the string is returned in the char * 911 * pointed to by str. The length of the string not including the trailing 912 * NUL is returned on success, -1 if the requested string capability 913 * couldn't be found, -2 if a system error was encountered (storage 914 * allocation failure). 915 */ 916 int 917 cgetstr(char *buf, const char *cap, char **str) 918 { 919 u_int m_room; 920 const char *bp; 921 char *mp; 922 ptrdiff_t len; 923 char *mem, *newmem; 924 925 _DIAGASSERT(buf != NULL); 926 _DIAGASSERT(cap != NULL); 927 _DIAGASSERT(str != NULL); 928 929 /* 930 * Find string capability cap 931 */ 932 bp = cgetcap(buf, cap, '='); 933 if (bp == NULL) 934 return -1; 935 936 /* 937 * Conversion / storage allocation loop ... Allocate memory in 938 * chunks SFRAG in size. 939 */ 940 if ((mem = malloc(SFRAG)) == NULL) { 941 errno = ENOMEM; 942 return -2; /* couldn't even allocate the first fragment */ 943 } 944 m_room = SFRAG; 945 mp = mem; 946 947 while (*bp != ':' && *bp != '\0') { 948 /* 949 * Loop invariants: 950 * There is always room for one more character in mem. 951 * Mp always points just past last character in mem. 952 * Bp always points at next character in buf. 953 */ 954 if (*bp == '^') { 955 bp++; 956 if (*bp == ':' || *bp == '\0') 957 break; /* drop unfinished escape */ 958 *mp++ = *bp++ & 037; 959 } else if (*bp == '\\') { 960 bp++; 961 if (*bp == ':' || *bp == '\0') 962 break; /* drop unfinished escape */ 963 if ('0' <= *bp && *bp <= '7') { 964 int n, i; 965 966 n = 0; 967 i = 3; /* maximum of three octal digits */ 968 do { 969 n = n * 8 + (*bp++ - '0'); 970 } while (--i && '0' <= *bp && *bp <= '7'); 971 *mp++ = n; 972 } 973 else switch (*bp++) { 974 case 'b': case 'B': 975 *mp++ = '\b'; 976 break; 977 case 't': case 'T': 978 *mp++ = '\t'; 979 break; 980 case 'n': case 'N': 981 *mp++ = '\n'; 982 break; 983 case 'f': case 'F': 984 *mp++ = '\f'; 985 break; 986 case 'r': case 'R': 987 *mp++ = '\r'; 988 break; 989 case 'e': case 'E': 990 *mp++ = ESC; 991 break; 992 case 'c': case 'C': 993 *mp++ = ':'; 994 break; 995 default: 996 /* 997 * Catches '\', '^', and 998 * everything else. 999 */ 1000 *mp++ = *(bp-1); 1001 break; 1002 } 1003 } else 1004 *mp++ = *bp++; 1005 m_room--; 1006 1007 /* 1008 * Enforce loop invariant: if no room left in current 1009 * buffer, try to get some more. 1010 */ 1011 if (m_room == 0) { 1012 size_t size = mp - mem; 1013 1014 if ((newmem = realloc(mem, size + SFRAG)) == NULL) { 1015 free(mem); 1016 return -2; 1017 } 1018 mem = newmem; 1019 m_room = SFRAG; 1020 mp = mem + size; 1021 } 1022 } 1023 *mp++ = '\0'; /* loop invariant let's us do this */ 1024 m_room--; 1025 len = mp - mem - 1; 1026 1027 /* 1028 * Give back any extra memory and return value and success. 1029 */ 1030 if (m_room != 0) { 1031 if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) { 1032 free(mem); 1033 return -2; 1034 } 1035 mem = newmem; 1036 } 1037 *str = mem; 1038 _DIAGASSERT(__type_fit(int, len)); 1039 return (int)len; 1040 } 1041 1042 /* 1043 * Cgetustr retrieves the value of the string capability cap from the 1044 * capability record pointed to by buf. The difference between cgetustr() 1045 * and cgetstr() is that cgetustr does not decode escapes but rather treats 1046 * all characters literally. A pointer to a NUL terminated malloc'd 1047 * copy of the string is returned in the char pointed to by str. The 1048 * length of the string not including the trailing NUL is returned on success, 1049 * -1 if the requested string capability couldn't be found, -2 if a system 1050 * error was encountered (storage allocation failure). 1051 */ 1052 int 1053 cgetustr(char *buf, const char *cap, char **str) 1054 { 1055 u_int m_room; 1056 const char *bp; 1057 char *mp; 1058 size_t len; 1059 char *mem, *newmem; 1060 1061 _DIAGASSERT(buf != NULL); 1062 _DIAGASSERT(cap != NULL); 1063 _DIAGASSERT(str != NULL); 1064 1065 /* 1066 * Find string capability cap 1067 */ 1068 if ((bp = cgetcap(buf, cap, '=')) == NULL) 1069 return -1; 1070 1071 /* 1072 * Conversion / storage allocation loop ... Allocate memory in 1073 * chunks SFRAG in size. 1074 */ 1075 if ((mem = malloc(SFRAG)) == NULL) { 1076 errno = ENOMEM; 1077 return -2; /* couldn't even allocate the first fragment */ 1078 } 1079 m_room = SFRAG; 1080 mp = mem; 1081 1082 while (*bp != ':' && *bp != '\0') { 1083 /* 1084 * Loop invariants: 1085 * There is always room for one more character in mem. 1086 * Mp always points just past last character in mem. 1087 * Bp always points at next character in buf. 1088 */ 1089 *mp++ = *bp++; 1090 m_room--; 1091 1092 /* 1093 * Enforce loop invariant: if no room left in current 1094 * buffer, try to get some more. 1095 */ 1096 if (m_room == 0) { 1097 size_t size = mp - mem; 1098 1099 if ((newmem = realloc(mem, size + SFRAG)) == NULL) { 1100 free(mem); 1101 return -2; 1102 } 1103 mem = newmem; 1104 m_room = SFRAG; 1105 mp = mem + size; 1106 } 1107 } 1108 *mp++ = '\0'; /* loop invariant let's us do this */ 1109 m_room--; 1110 len = mp - mem - 1; 1111 1112 /* 1113 * Give back any extra memory and return value and success. 1114 */ 1115 if (m_room != 0) { 1116 if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) { 1117 free(mem); 1118 return -2; 1119 } 1120 mem = newmem; 1121 } 1122 *str = mem; 1123 _DIAGASSERT(__type_fit(int, len)); 1124 return (int)len; 1125 } 1126 1127 /* 1128 * Cgetnum retrieves the value of the numeric capability cap from the 1129 * capability record pointed to by buf. The numeric value is returned in 1130 * the long pointed to by num. 0 is returned on success, -1 if the requested 1131 * numeric capability couldn't be found. 1132 */ 1133 int 1134 cgetnum(char *buf, const char *cap, long *num) 1135 { 1136 long n; 1137 int base, digit; 1138 const char *bp; 1139 1140 _DIAGASSERT(buf != NULL); 1141 _DIAGASSERT(cap != NULL); 1142 _DIAGASSERT(num != NULL); 1143 1144 /* 1145 * Find numeric capability cap 1146 */ 1147 bp = cgetcap(buf, cap, '#'); 1148 if (bp == NULL) 1149 return -1; 1150 1151 /* 1152 * Look at value and determine numeric base: 1153 * 0x... or 0X... hexadecimal, 1154 * else 0... octal, 1155 * else decimal. 1156 */ 1157 if (*bp == '0') { 1158 bp++; 1159 if (*bp == 'x' || *bp == 'X') { 1160 bp++; 1161 base = 16; 1162 } else 1163 base = 8; 1164 } else 1165 base = 10; 1166 1167 /* 1168 * Conversion loop ... 1169 */ 1170 n = 0; 1171 for (;;) { 1172 if ('0' <= *bp && *bp <= '9') 1173 digit = *bp - '0'; 1174 else if ('a' <= *bp && *bp <= 'f') 1175 digit = 10 + *bp - 'a'; 1176 else if ('A' <= *bp && *bp <= 'F') 1177 digit = 10 + *bp - 'A'; 1178 else 1179 break; 1180 1181 if (digit >= base) 1182 break; 1183 1184 n = n * base + digit; 1185 bp++; 1186 } 1187 1188 /* 1189 * Return value and success. 1190 */ 1191 *num = n; 1192 return 0; 1193 } 1194 1195 1196 /* 1197 * Compare name field of record. 1198 */ 1199 static int 1200 nfcmp(char *nf, char *rec) 1201 { 1202 char *cp, tmp; 1203 int ret; 1204 1205 _DIAGASSERT(nf != NULL); 1206 _DIAGASSERT(rec != NULL); 1207 1208 for (cp = rec; *cp != ':'; cp++) 1209 continue; 1210 1211 tmp = *(cp + 1); 1212 *(cp + 1) = '\0'; 1213 ret = strcmp(nf, rec); 1214 *(cp + 1) = tmp; 1215 1216 return ret; 1217 } 1218