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