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