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