1 /* $NetBSD: getcap.c,v 1.45 2006/04/02 03:26:03 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.45 2006/04/02 03:26:03 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 <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 cbuf = malloc(clen + 1); 330 memmove(cbuf, record, clen + 1); 331 if (capdbp->close(capdbp) < 0) { 332 int serrno = errno; 333 334 free(cbuf); 335 errno = serrno; 336 return (-2); 337 } 338 *len = clen; 339 *cap = cbuf; 340 return (retval); 341 } else 342 #endif 343 { 344 fd = open(*db_p, O_RDONLY, 0); 345 if (fd < 0) { 346 /* No error on unfound file. */ 347 continue; 348 } 349 myfd = 1; 350 } 351 } 352 /* 353 * Find the requested capability record ... 354 */ 355 { 356 char buf[BUFSIZ]; 357 char *b_end, *bp, *cp; 358 int c, slash; 359 360 /* 361 * Loop invariants: 362 * There is always room for one more character in record. 363 * R_end always points just past end of record. 364 * Rp always points just past last character in record. 365 * B_end always points just past last character in buf. 366 * Bp always points at next character in buf. 367 * Cp remembers where the last colon was. 368 */ 369 b_end = buf; 370 bp = buf; 371 cp = 0; 372 slash = 0; 373 for (;;) { 374 375 /* 376 * Read in a line implementing (\, newline) 377 * line continuation. 378 */ 379 rp = record; 380 for (;;) { 381 if (bp >= b_end) { 382 int n; 383 384 n = read(fd, buf, sizeof(buf)); 385 if (n <= 0) { 386 if (myfd) 387 (void)close(fd); 388 if (n < 0) { 389 int serrno = errno; 390 391 free(record); 392 errno = serrno; 393 return (-2); 394 } else { 395 fd = -1; 396 eof = 1; 397 break; 398 } 399 } 400 b_end = buf+n; 401 bp = buf; 402 } 403 404 c = *bp++; 405 if (c == '\n') { 406 if (slash) { 407 slash = 0; 408 rp--; 409 continue; 410 } else 411 break; 412 } 413 if (slash) { 414 slash = 0; 415 cp = 0; 416 } 417 if (c == ':') { 418 /* 419 * If the field was `empty' (i.e. 420 * contained only white space), back up 421 * to the colon (eliminating the 422 * field). 423 */ 424 if (cp) 425 rp = cp; 426 else 427 cp = rp; 428 } else if (c == '\\') { 429 slash = 1; 430 } else if (c != ' ' && c != '\t') { 431 /* 432 * Forget where the colon was, as this 433 * is not an empty field. 434 */ 435 cp = 0; 436 } 437 *rp++ = c; 438 439 /* 440 * Enforce loop invariant: if no room 441 * left in record buffer, try to get 442 * some more. 443 */ 444 if (rp >= r_end) { 445 u_int pos; 446 size_t newsize; 447 448 pos = rp - record; 449 newsize = r_end - record + BFRAG; 450 newrecord = realloc(record, newsize); 451 if (newrecord == NULL) { 452 free(record); 453 if (myfd) 454 (void)close(fd); 455 errno = ENOMEM; 456 return (-2); 457 } 458 record = newrecord; 459 r_end = record + newsize; 460 rp = record + pos; 461 } 462 } 463 /* Eliminate any white space after the last colon. */ 464 if (cp) 465 rp = cp + 1; 466 /* Loop invariant lets us do this. */ 467 *rp++ = '\0'; 468 469 /* 470 * If encountered eof check next file. 471 */ 472 if (eof) 473 break; 474 475 /* 476 * Toss blank lines and comments. 477 */ 478 if (*record == '\0' || *record == '#') 479 continue; 480 481 /* 482 * See if this is the record we want ... 483 */ 484 if (cgetmatch(record, name) == 0) { 485 if (nfield == NULL || !nfcmp(nfield, record)) { 486 foundit = 1; 487 break; /* found it! */ 488 } 489 } 490 } 491 } 492 if (foundit) 493 break; 494 } 495 496 if (!foundit) 497 return (-1); 498 499 /* 500 * Got the capability record, but now we have to expand all tc=name 501 * references in it ... 502 */ 503 tc_exp: { 504 char *newicap, *s; 505 size_t ilen, newilen; 506 int diff, iret, tclen; 507 char *icap, *scan, *tc, *tcstart, *tcend; 508 509 /* 510 * Loop invariants: 511 * There is room for one more character in record. 512 * R_end points just past end of record. 513 * Rp points just past last character in record. 514 * Scan points at remainder of record that needs to be 515 * scanned for tc=name constructs. 516 */ 517 scan = record; 518 tc_not_resolved = 0; 519 for (;;) { 520 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 521 break; 522 523 /* 524 * Find end of tc=name and stomp on the trailing `:' 525 * (if present) so we can use it to call ourselves. 526 */ 527 s = tc; 528 for (;;) 529 if (*s == '\0') 530 break; 531 else 532 if (*s++ == ':') { 533 *(s - 1) = '\0'; 534 break; 535 } 536 tcstart = tc - 3; 537 tclen = s - tcstart; 538 tcend = s; 539 540 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 541 NULL); 542 newicap = icap; /* Put into a register. */ 543 newilen = ilen; 544 if (iret != 0) { 545 /* an error */ 546 if (iret < -1) { 547 if (myfd) 548 (void)close(fd); 549 free(record); 550 return (iret); 551 } 552 if (iret == 1) 553 tc_not_resolved = 1; 554 /* couldn't resolve tc */ 555 if (iret == -1) { 556 *(s - 1) = ':'; 557 scan = s - 1; 558 tc_not_resolved = 1; 559 continue; 560 561 } 562 } 563 /* not interested in name field of tc'ed record */ 564 s = newicap; 565 for (;;) 566 if (*s == '\0') 567 break; 568 else 569 if (*s++ == ':') 570 break; 571 newilen -= s - newicap; 572 newicap = s; 573 574 /* make sure interpolated record is `:'-terminated */ 575 s += newilen; 576 if (*(s-1) != ':') { 577 *s = ':'; /* overwrite NUL with : */ 578 newilen++; 579 } 580 581 /* 582 * Make sure there's enough room to insert the 583 * new record. 584 */ 585 diff = newilen - tclen; 586 if (diff >= r_end - rp) { 587 u_int pos, tcpos, tcposend; 588 size_t newsize; 589 590 pos = rp - record; 591 newsize = r_end - record + diff + BFRAG; 592 tcpos = tcstart - record; 593 tcposend = tcend - record; 594 newrecord = realloc(record, newsize); 595 if (newrecord == NULL) { 596 free(record); 597 if (myfd) 598 (void)close(fd); 599 free(icap); 600 errno = ENOMEM; 601 return (-2); 602 } 603 record = newrecord; 604 r_end = record + newsize; 605 rp = record + pos; 606 tcstart = record + tcpos; 607 tcend = record + tcposend; 608 } 609 610 /* 611 * Insert tc'ed record into our record. 612 */ 613 s = tcstart + newilen; 614 memmove(s, tcend, (size_t)(rp - tcend)); 615 memmove(tcstart, newicap, newilen); 616 rp += diff; 617 free(icap); 618 619 /* 620 * Start scan on `:' so next cgetcap works properly 621 * (cgetcap always skips first field). 622 */ 623 scan = s-1; 624 } 625 626 } 627 /* 628 * Close file (if we opened it), give back any extra memory, and 629 * return capability, length and success. 630 */ 631 if (myfd) 632 (void)close(fd); 633 *len = rp - record - 1; /* don't count NUL */ 634 if (r_end > rp) { 635 if ((newrecord = 636 realloc(record, (size_t)(rp - record))) == NULL) { 637 free(record); 638 errno = ENOMEM; 639 return (-2); 640 } 641 record = newrecord; 642 } 643 644 *cap = record; 645 if (tc_not_resolved) 646 return (1); 647 return (0); 648 } 649 650 #ifndef SMALL 651 static int 652 cdbget(DB *capdbp, char **bp, const char *name) 653 { 654 DBT key; 655 DBT data; 656 657 _DIAGASSERT(capdbp != NULL); 658 _DIAGASSERT(bp != NULL); 659 _DIAGASSERT(name != NULL); 660 661 key.data = __UNCONST(name); 662 key.size = strlen(name); 663 664 for (;;) { 665 /* Get the reference. */ 666 switch(capdbp->get(capdbp, &key, &data, 0)) { 667 case -1: 668 return (-2); 669 case 1: 670 return (-1); 671 } 672 673 /* If not an index to another record, leave. */ 674 if (((char *)data.data)[0] != SHADOW) 675 break; 676 677 key.data = (char *)data.data + 1; 678 key.size = data.size - 1; 679 } 680 681 *bp = (char *)data.data + 1; 682 return (((char *)(data.data))[0] == TCERR ? 1 : 0); 683 } 684 #endif 685 686 /* 687 * Cgetmatch will return 0 if name is one of the names of the capability 688 * record buf, -1 if not. 689 */ 690 int 691 cgetmatch(const char *buf, const char *name) 692 { 693 const char *np, *bp; 694 695 _DIAGASSERT(buf != NULL); 696 _DIAGASSERT(name != NULL); 697 698 /* 699 * Start search at beginning of record. 700 */ 701 bp = buf; 702 for (;;) { 703 /* 704 * Try to match a record name. 705 */ 706 np = name; 707 for (;;) 708 if (*np == '\0') { 709 if (*bp == '|' || *bp == ':' || *bp == '\0') 710 return (0); 711 else 712 break; 713 } else 714 if (*bp++ != *np++) 715 break; 716 717 /* 718 * Match failed, skip to next name in record. 719 */ 720 if (bp > buf) 721 bp--; /* a '|' or ':' may have stopped the match */ 722 else 723 return (-1); 724 for (;;) 725 if (*bp == '\0' || *bp == ':') 726 return (-1); /* match failed totally */ 727 else 728 if (*bp++ == '|') 729 break; /* found next name */ 730 } 731 } 732 733 int 734 cgetfirst(char **buf, const char * const *db_array) 735 { 736 737 _DIAGASSERT(buf != NULL); 738 _DIAGASSERT(db_array != NULL); 739 740 (void)cgetclose(); 741 return (cgetnext(buf, db_array)); 742 } 743 744 static FILE *pfp; 745 static int slash; 746 static const char * const *dbp; 747 748 int 749 cgetclose(void) 750 { 751 if (pfp != NULL) { 752 (void)fclose(pfp); 753 pfp = NULL; 754 } 755 dbp = NULL; 756 gottoprec = 0; 757 slash = 0; 758 return(0); 759 } 760 761 /* 762 * Cgetnext() gets either the first or next entry in the logical database 763 * specified by db_array. It returns 0 upon completion of the database, 1 764 * upon returning an entry with more remaining, and -1 if an error occurs. 765 */ 766 /* coverity[+alloc : arg-*0] */ 767 int 768 cgetnext(char **bp, const char * const *db_array) 769 { 770 size_t len = 0; 771 int status, done; 772 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 773 size_t dummy; 774 775 _DIAGASSERT(bp != NULL); 776 _DIAGASSERT(db_array != NULL); 777 778 if (dbp == NULL) 779 dbp = db_array; 780 781 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 782 (void)cgetclose(); 783 return (-1); 784 } 785 for(;;) { 786 if (toprec && !gottoprec) { 787 gottoprec = 1; 788 line = toprec; 789 } else { 790 line = fgetln(pfp, &len); 791 if (line == NULL) { 792 if (pfp == NULL) 793 return -1; 794 if (ferror(pfp)) { 795 (void)cgetclose(); 796 return (-1); 797 } else { 798 (void)fclose(pfp); 799 pfp = NULL; 800 if (*++dbp == NULL) { 801 (void)cgetclose(); 802 return (0); 803 } else if ((pfp = 804 fopen(*dbp, "r")) == NULL) { 805 (void)cgetclose(); 806 return (-1); 807 } else 808 continue; 809 } 810 } else 811 line[len - 1] = '\0'; 812 if (len == 1) { 813 slash = 0; 814 continue; 815 } 816 if (isspace((unsigned char)*line) || 817 *line == ':' || *line == '#' || slash) { 818 if (line[len - 2] == '\\') 819 slash = 1; 820 else 821 slash = 0; 822 continue; 823 } 824 if (line[len - 2] == '\\') 825 slash = 1; 826 else 827 slash = 0; 828 } 829 830 831 /* 832 * Line points to a name line. 833 */ 834 if (len > sizeof(nbuf)) 835 return -1; 836 done = 0; 837 np = nbuf; 838 for (;;) { 839 for (cp = line; *cp != '\0'; cp++) { 840 if (*cp == ':') { 841 *np++ = ':'; 842 done = 1; 843 break; 844 } 845 if (*cp == '\\') 846 break; 847 *np++ = *cp; 848 } 849 if (done) { 850 *np = '\0'; 851 break; 852 } else { /* name field extends beyond the line */ 853 line = fgetln(pfp, &len); 854 if (line == NULL && pfp) { 855 if (ferror(pfp)) { 856 (void)cgetclose(); 857 return (-1); 858 } 859 (void)fclose(pfp); 860 pfp = NULL; 861 *np = '\0'; 862 break; 863 } else 864 line[len - 1] = '\0'; 865 } 866 } 867 if (len > sizeof(buf)) 868 return -1; 869 rp = buf; 870 for(cp = nbuf; *cp != '\0'; cp++) 871 if (*cp == '|' || *cp == ':') 872 break; 873 else 874 *rp++ = *cp; 875 876 *rp = '\0'; 877 /* 878 * XXX 879 * Last argument of getent here should be nbuf if we want true 880 * sequential access in the case of duplicates. 881 * With NULL, getent will return the first entry found 882 * rather than the duplicate entry record. This is a 883 * matter of semantics that should be resolved. 884 */ 885 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 886 if (status == -2 || status == -3) 887 (void)cgetclose(); 888 889 return (status + 1); 890 } 891 /* NOTREACHED */ 892 } 893 894 /* 895 * Cgetstr retrieves the value of the string capability cap from the 896 * capability record pointed to by buf. A pointer to a decoded, NUL 897 * terminated, malloc'd copy of the string is returned in the char * 898 * pointed to by str. The length of the string not including the trailing 899 * NUL is returned on success, -1 if the requested string capability 900 * couldn't be found, -2 if a system error was encountered (storage 901 * allocation failure). 902 */ 903 int 904 cgetstr(char *buf, const char *cap, char **str) 905 { 906 u_int m_room; 907 const char *bp; 908 char *mp; 909 int len; 910 char *mem, *newmem; 911 912 _DIAGASSERT(buf != NULL); 913 _DIAGASSERT(cap != NULL); 914 _DIAGASSERT(str != NULL); 915 916 /* 917 * Find string capability cap 918 */ 919 bp = cgetcap(buf, cap, '='); 920 if (bp == NULL) 921 return (-1); 922 923 /* 924 * Conversion / storage allocation loop ... Allocate memory in 925 * chunks SFRAG in size. 926 */ 927 if ((mem = malloc(SFRAG)) == NULL) { 928 errno = ENOMEM; 929 return (-2); /* couldn't even allocate the first fragment */ 930 } 931 m_room = SFRAG; 932 mp = mem; 933 934 while (*bp != ':' && *bp != '\0') { 935 /* 936 * Loop invariants: 937 * There is always room for one more character in mem. 938 * Mp always points just past last character in mem. 939 * Bp always points at next character in buf. 940 */ 941 if (*bp == '^') { 942 bp++; 943 if (*bp == ':' || *bp == '\0') 944 break; /* drop unfinished escape */ 945 *mp++ = *bp++ & 037; 946 } else if (*bp == '\\') { 947 bp++; 948 if (*bp == ':' || *bp == '\0') 949 break; /* drop unfinished escape */ 950 if ('0' <= *bp && *bp <= '7') { 951 int n, i; 952 953 n = 0; 954 i = 3; /* maximum of three octal digits */ 955 do { 956 n = n * 8 + (*bp++ - '0'); 957 } while (--i && '0' <= *bp && *bp <= '7'); 958 *mp++ = n; 959 } 960 else switch (*bp++) { 961 case 'b': case 'B': 962 *mp++ = '\b'; 963 break; 964 case 't': case 'T': 965 *mp++ = '\t'; 966 break; 967 case 'n': case 'N': 968 *mp++ = '\n'; 969 break; 970 case 'f': case 'F': 971 *mp++ = '\f'; 972 break; 973 case 'r': case 'R': 974 *mp++ = '\r'; 975 break; 976 case 'e': case 'E': 977 *mp++ = ESC; 978 break; 979 case 'c': case 'C': 980 *mp++ = ':'; 981 break; 982 default: 983 /* 984 * Catches '\', '^', and 985 * everything else. 986 */ 987 *mp++ = *(bp-1); 988 break; 989 } 990 } else 991 *mp++ = *bp++; 992 m_room--; 993 994 /* 995 * Enforce loop invariant: if no room left in current 996 * buffer, try to get some more. 997 */ 998 if (m_room == 0) { 999 size_t size = mp - mem; 1000 1001 if ((newmem = realloc(mem, size + SFRAG)) == NULL) { 1002 free(mem); 1003 return (-2); 1004 } 1005 mem = newmem; 1006 m_room = SFRAG; 1007 mp = mem + size; 1008 } 1009 } 1010 *mp++ = '\0'; /* loop invariant let's us do this */ 1011 m_room--; 1012 len = mp - mem - 1; 1013 1014 /* 1015 * Give back any extra memory and return value and success. 1016 */ 1017 if (m_room != 0) { 1018 if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) { 1019 free(mem); 1020 return (-2); 1021 } 1022 mem = newmem; 1023 } 1024 *str = mem; 1025 return (len); 1026 } 1027 1028 /* 1029 * Cgetustr retrieves the value of the string capability cap from the 1030 * capability record pointed to by buf. The difference between cgetustr() 1031 * and cgetstr() is that cgetustr does not decode escapes but rather treats 1032 * all characters literally. A pointer to a NUL terminated malloc'd 1033 * copy of the string is returned in the char pointed to by str. The 1034 * length of the string not including the trailing NUL is returned on success, 1035 * -1 if the requested string capability couldn't be found, -2 if a system 1036 * error was encountered (storage allocation failure). 1037 */ 1038 int 1039 cgetustr(char *buf, const char *cap, char **str) 1040 { 1041 u_int m_room; 1042 const char *bp; 1043 char *mp; 1044 int len; 1045 char *mem, *newmem; 1046 1047 _DIAGASSERT(buf != NULL); 1048 _DIAGASSERT(cap != NULL); 1049 _DIAGASSERT(str != NULL); 1050 1051 /* 1052 * Find string capability cap 1053 */ 1054 if ((bp = cgetcap(buf, cap, '=')) == NULL) 1055 return (-1); 1056 1057 /* 1058 * Conversion / storage allocation loop ... Allocate memory in 1059 * chunks SFRAG in size. 1060 */ 1061 if ((mem = malloc(SFRAG)) == NULL) { 1062 errno = ENOMEM; 1063 return (-2); /* couldn't even allocate the first fragment */ 1064 } 1065 m_room = SFRAG; 1066 mp = mem; 1067 1068 while (*bp != ':' && *bp != '\0') { 1069 /* 1070 * Loop invariants: 1071 * There is always room for one more character in mem. 1072 * Mp always points just past last character in mem. 1073 * Bp always points at next character in buf. 1074 */ 1075 *mp++ = *bp++; 1076 m_room--; 1077 1078 /* 1079 * Enforce loop invariant: if no room left in current 1080 * buffer, try to get some more. 1081 */ 1082 if (m_room == 0) { 1083 size_t size = mp - mem; 1084 1085 if ((newmem = realloc(mem, size + SFRAG)) == NULL) { 1086 free(mem); 1087 return (-2); 1088 } 1089 mem = newmem; 1090 m_room = SFRAG; 1091 mp = mem + size; 1092 } 1093 } 1094 *mp++ = '\0'; /* loop invariant let's us do this */ 1095 m_room--; 1096 len = mp - mem - 1; 1097 1098 /* 1099 * Give back any extra memory and return value and success. 1100 */ 1101 if (m_room != 0) { 1102 if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) { 1103 free(mem); 1104 return (-2); 1105 } 1106 mem = newmem; 1107 } 1108 *str = mem; 1109 return (len); 1110 } 1111 1112 /* 1113 * Cgetnum retrieves the value of the numeric capability cap from the 1114 * capability record pointed to by buf. The numeric value is returned in 1115 * the long pointed to by num. 0 is returned on success, -1 if the requested 1116 * numeric capability couldn't be found. 1117 */ 1118 int 1119 cgetnum(char *buf, const char *cap, long *num) 1120 { 1121 long n; 1122 int base, digit; 1123 const char *bp; 1124 1125 _DIAGASSERT(buf != NULL); 1126 _DIAGASSERT(cap != NULL); 1127 _DIAGASSERT(num != NULL); 1128 1129 /* 1130 * Find numeric capability cap 1131 */ 1132 bp = cgetcap(buf, cap, '#'); 1133 if (bp == NULL) 1134 return (-1); 1135 1136 /* 1137 * Look at value and determine numeric base: 1138 * 0x... or 0X... hexadecimal, 1139 * else 0... octal, 1140 * else decimal. 1141 */ 1142 if (*bp == '0') { 1143 bp++; 1144 if (*bp == 'x' || *bp == 'X') { 1145 bp++; 1146 base = 16; 1147 } else 1148 base = 8; 1149 } else 1150 base = 10; 1151 1152 /* 1153 * Conversion loop ... 1154 */ 1155 n = 0; 1156 for (;;) { 1157 if ('0' <= *bp && *bp <= '9') 1158 digit = *bp - '0'; 1159 else if ('a' <= *bp && *bp <= 'f') 1160 digit = 10 + *bp - 'a'; 1161 else if ('A' <= *bp && *bp <= 'F') 1162 digit = 10 + *bp - 'A'; 1163 else 1164 break; 1165 1166 if (digit >= base) 1167 break; 1168 1169 n = n * base + digit; 1170 bp++; 1171 } 1172 1173 /* 1174 * Return value and success. 1175 */ 1176 *num = n; 1177 return (0); 1178 } 1179 1180 1181 /* 1182 * Compare name field of record. 1183 */ 1184 static int 1185 nfcmp(char *nf, char *rec) 1186 { 1187 char *cp, tmp; 1188 int ret; 1189 1190 _DIAGASSERT(nf != NULL); 1191 _DIAGASSERT(rec != NULL); 1192 1193 for (cp = rec; *cp != ':'; cp++) 1194 ; 1195 1196 tmp = *(cp + 1); 1197 *(cp + 1) = '\0'; 1198 ret = strcmp(nf, rec); 1199 *(cp + 1) = tmp; 1200 1201 return (ret); 1202 } 1203