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