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