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