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