1/* $OpenBSD: getcap.c,v 1.37 2024/01/22 17:22:58 deraadt 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 56static size_t topreclen; /* toprec length */ 57static char *toprec; /* Additional record specified by cgetset() */ 58static int gottoprec; /* Flag indicating retrieval of toprecord */ 59 60static int cdbget(DB *, char **, const char *); 61static int getent(char **, u_int *, char **, FILE *, const char *, int, char *); 62static int nfcmp(const char *, char *); 63 64static 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 */ 71int 72cgetusedb(int new_usedb) 73{ 74 int old_usedb = usedb; 75 76 usedb = new_usedb; 77 return(old_usedb); 78} 79DEF_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 */ 86int 87cgetset(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} 102DEF_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 */ 116char * 117cgetcap(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} 157DEF_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 */ 168int 169cgetent(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} 175DEF_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 */ 195static int 196getent(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 fseeko(fp, 0, 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 >= 0 && clen < sizeof(pbuf) && usedb && 255 (capdbp = __hash_open(pbuf, O_RDONLY, 0, NULL, 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 */ 406tc_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 = realloc(record, rp - record)) == NULL) { 541 free(record); 542 errno = ENOMEM; 543 return (-2); 544 } 545 record = nrecord; 546 } 547 *cap = record; 548 if (tc_not_resolved) 549 return (1); 550 return (0); 551} 552 553static int 554cdbget(DB *capdbp, char **bp, const char *name) 555{ 556 DBT key, data; 557 558 key.data = (void *)name; 559 key.size = strlen(name); 560 561 for (;;) { 562 /* Get the reference. */ 563 switch(capdbp->get(capdbp, &key, &data, 0)) { 564 case -1: 565 return (-2); 566 case 1: 567 return (-1); 568 } 569 570 /* If not an index to another record, leave. */ 571 if (((char *)data.data)[0] != SHADOW) 572 break; 573 574 key.data = (char *)data.data + 1; 575 key.size = data.size - 1; 576 } 577 578 *bp = (char *)data.data + 1; 579 return (((char *)(data.data))[0] == TCERR ? 1 : 0); 580} 581 582/* 583 * Cgetmatch will return 0 if name is one of the names of the capability 584 * record buf, -1 if not. 585 */ 586int 587cgetmatch(char *buf, const char *name) 588{ 589 char *bp; 590 const char *np; 591 592 if (*name == '\0') 593 return (-1); 594 /* 595 * Start search at beginning of record. 596 */ 597 bp = buf; 598 for (;;) { 599 /* 600 * Try to match a record name. 601 */ 602 np = name; 603 for (;;) 604 if (*np == '\0') { 605 if (*bp == '|' || *bp == ':' || *bp == '\0') 606 return (0); 607 else 608 break; 609 } else 610 if (*bp++ != *np++) 611 break; 612 613 /* 614 * Match failed, skip to next name in record. 615 */ 616 bp--; /* a '|' or ':' may have stopped the match */ 617 for (;;) 618 if (*bp == '\0' || *bp == ':') 619 return (-1); /* match failed totally */ 620 else 621 if (*bp++ == '|') 622 break; /* found next name */ 623 } 624} 625DEF_WEAK(cgetmatch); 626 627int 628cgetfirst(char **buf, char **db_array) 629{ 630 631 (void)cgetclose(); 632 return (cgetnext(buf, db_array)); 633} 634DEF_WEAK(cgetfirst); 635 636static FILE *pfp; 637static int slash; 638static char **dbp; 639 640int 641cgetclose(void) 642{ 643 644 if (pfp != NULL) { 645 (void)fclose(pfp); 646 pfp = NULL; 647 } 648 dbp = NULL; 649 gottoprec = 0; 650 slash = 0; 651 return(0); 652} 653DEF_WEAK(cgetclose); 654 655/* 656 * Cgetnext() gets either the first or next entry in the logical database 657 * specified by db_array. It returns 0 upon completion of the database, 1 658 * upon returning an entry with more remaining, and -1 if an error occurs. 659 */ 660int 661cgetnext(char **cap, char **db_array) 662{ 663 size_t len, otopreclen = topreclen; 664 int c, serrno, status = -1; 665 char buf[BUFSIZ], nbuf[BSIZE]; 666 char *b_end, *bp, *r_end, *rp; 667 char *record = NULL; 668 char *otoprec = toprec; 669 u_int dummy; 670 off_t pos; 671 672 if (dbp == NULL) 673 dbp = db_array; 674 675 if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) 676 goto done; 677 678 /* 679 * Check if we have an unused top record from cgetset(). 680 */ 681 if (toprec && !gottoprec) { 682 gottoprec = 1; 683 record = toprec; 684 goto lookup; 685 } 686 687 /* 688 * Allocate first chunk of memory. 689 */ 690 if ((record = malloc(BFRAG)) == NULL) 691 goto done; 692 r_end = record + BFRAG; 693 694 /* 695 * Find the next capability record 696 */ 697 /* 698 * Loop invariants: 699 * There is always room for one more character in record. 700 * R_end always points just past end of record. 701 * Rp always points just past last character in record. 702 * B_end always points just past last character in buf. 703 * Bp always points at next character in buf. 704 */ 705 b_end = buf; 706 bp = buf; 707 for (;;) { 708 /* 709 * Read in a line implementing (\, newline) 710 * line continuation. 711 */ 712 rp = record; 713 for (;;) { 714 if (bp >= b_end) { 715 size_t n; 716 717 n = fread(buf, 1, sizeof(buf), pfp); 718 if (n == 0) { 719 if (ferror(pfp)) 720 goto done; 721 (void)fclose(pfp); 722 pfp = NULL; 723 if (*++dbp == NULL) { 724 status = 0; 725 goto done; 726 } else if ((pfp = 727 fopen(*dbp, "re")) == NULL) { 728 goto done; 729 } else 730 continue; 731 } 732 b_end = buf + n; 733 bp = buf; 734 } 735 736 c = *bp++; 737 if (c == '\n') { 738 if (rp > record && *(rp-1) == '\\') { 739 rp--; 740 continue; 741 } else 742 break; 743 } 744 *rp++ = c; 745 746 /* 747 * Enforce loop invariant: if no room 748 * left in record buffer, try to get 749 * some more. 750 */ 751 if (rp >= r_end) { 752 size_t newsize, off; 753 char *nrecord; 754 755 off = rp - record; 756 newsize = r_end - record + BFRAG; 757 nrecord = realloc(record, newsize); 758 if (nrecord == NULL) 759 goto done; 760 record = nrecord; 761 r_end = record + newsize; 762 rp = record + off; 763 } 764 } 765 /* loop invariant lets us do this */ 766 *rp++ = '\0'; 767 768 /* 769 * If not blank or comment, set toprec and topreclen so 770 * getent() doesn't have to re-parse the file to find it. 771 */ 772 if (*record != '\0' && *record != '#') { 773 /* Rewind to end of record */ 774 fseeko(pfp, bp - b_end, SEEK_CUR); 775 toprec = record; 776 topreclen = rp - record; 777 break; 778 } 779 } 780lookup: 781 /* extract name from record */ 782 len = strcspn(record, "|:"); 783 memcpy(nbuf, record, len); 784 nbuf[len] = '\0'; 785 786 /* return value of getent() is one less than cgetnext() */ 787 pos = ftello(pfp); 788 status = getent(cap, &dummy, dbp, pfp, nbuf, 0, NULL) + 1; 789 if (status > 0) 790 fseeko(pfp, pos, SEEK_SET); 791done: 792 serrno = errno; 793 if (toprec != otoprec) { 794 toprec = otoprec; 795 topreclen = otopreclen; 796 free(record); 797 } 798 if (status <= 0) 799 (void)cgetclose(); 800 errno = serrno; 801 802 return (status); 803} 804DEF_WEAK(cgetnext); 805 806/* 807 * Cgetstr retrieves the value of the string capability cap from the 808 * capability record pointed to by buf. A pointer to a decoded, NUL 809 * terminated, malloc'd copy of the string is returned in the char * 810 * pointed to by str. The length of the string not including the trailing 811 * NUL is returned on success, -1 if the requested string capability 812 * couldn't be found, -2 if a system error was encountered (storage 813 * allocation failure). 814 */ 815int 816cgetstr(char *buf, const char *cap, char **str) 817{ 818 u_int m_room; 819 char *bp, *mp; 820 int len; 821 char *mem; 822 823 /* 824 * Find string capability cap 825 */ 826 bp = cgetcap(buf, cap, '='); 827 if (bp == NULL) 828 return (-1); 829 830 /* 831 * Conversion / storage allocation loop ... Allocate memory in 832 * chunks SFRAG in size. 833 */ 834 if ((mem = malloc(SFRAG)) == NULL) 835 return (-2); /* couldn't even allocate the first fragment */ 836 m_room = SFRAG; 837 mp = mem; 838 839 while (*bp != ':' && *bp != '\0') { 840 /* 841 * Loop invariants: 842 * There is always room for one more character in mem. 843 * Mp always points just past last character in mem. 844 * Bp always points at next character in buf. 845 */ 846 if (*bp == '^') { 847 bp++; 848 if (*bp == ':' || *bp == '\0') 849 break; /* drop unfinished escape */ 850 *mp++ = *bp++ & 037; 851 } else if (*bp == '\\') { 852 bp++; 853 if (*bp == ':' || *bp == '\0') 854 break; /* drop unfinished escape */ 855 if ('0' <= *bp && *bp <= '7') { 856 int n, i; 857 858 n = 0; 859 i = 3; /* maximum of three octal digits */ 860 do { 861 n = n * 8 + (*bp++ - '0'); 862 } while (--i && '0' <= *bp && *bp <= '7'); 863 *mp++ = n; 864 } 865 else switch (*bp++) { 866 case 'b': case 'B': 867 *mp++ = '\b'; 868 break; 869 case 't': case 'T': 870 *mp++ = '\t'; 871 break; 872 case 'n': case 'N': 873 *mp++ = '\n'; 874 break; 875 case 'f': case 'F': 876 *mp++ = '\f'; 877 break; 878 case 'r': case 'R': 879 *mp++ = '\r'; 880 break; 881 case 'e': case 'E': 882 *mp++ = ESC; 883 break; 884 case 'c': case 'C': 885 *mp++ = ':'; 886 break; 887 default: 888 /* 889 * Catches '\', '^', and 890 * everything else. 891 */ 892 *mp++ = *(bp-1); 893 break; 894 } 895 } else 896 *mp++ = *bp++; 897 m_room--; 898 899 /* 900 * Enforce loop invariant: if no room left in current 901 * buffer, try to get some more. 902 */ 903 if (m_room == 0) { 904 size_t size = mp - mem; 905 char *nmem; 906 907 if ((nmem = realloc(mem, size + SFRAG)) == NULL) { 908 free(mem); 909 return (-2); 910 } 911 mem = nmem; 912 m_room = SFRAG; 913 mp = mem + size; 914 } 915 } 916 *mp++ = '\0'; /* loop invariant let's us do this */ 917 m_room--; 918 len = mp - mem - 1; 919 920 /* 921 * Give back any extra memory and return value and success. 922 */ 923 if (m_room != 0) { 924 char *nmem; 925 926 if ((nmem = realloc(mem, mp - mem)) == NULL) { 927 free(mem); 928 return (-2); 929 } 930 mem = nmem; 931 } 932 *str = mem; 933 return (len); 934} 935DEF_WEAK(cgetstr); 936 937/* 938 * Cgetustr retrieves the value of the string capability cap from the 939 * capability record pointed to by buf. The difference between cgetustr() 940 * and cgetstr() is that cgetustr does not decode escapes but rather treats 941 * all characters literally. A pointer to a NUL terminated malloc'd 942 * copy of the string is returned in the char pointed to by str. The 943 * length of the string not including the trailing NUL is returned on success, 944 * -1 if the requested string capability couldn't be found, -2 if a system 945 * error was encountered (storage allocation failure). 946 */ 947int 948cgetustr(char *buf, const char *cap, char **str) 949{ 950 u_int m_room; 951 char *bp, *mp; 952 int len; 953 char *mem; 954 955 /* 956 * Find string capability cap 957 */ 958 if ((bp = cgetcap(buf, cap, '=')) == NULL) 959 return (-1); 960 961 /* 962 * Conversion / storage allocation loop ... Allocate memory in 963 * chunks SFRAG in size. 964 */ 965 if ((mem = malloc(SFRAG)) == NULL) 966 return (-2); /* couldn't even allocate the first fragment */ 967 m_room = SFRAG; 968 mp = mem; 969 970 while (*bp != ':' && *bp != '\0') { 971 /* 972 * Loop invariants: 973 * There is always room for one more character in mem. 974 * Mp always points just past last character in mem. 975 * Bp always points at next character in buf. 976 */ 977 *mp++ = *bp++; 978 m_room--; 979 980 /* 981 * Enforce loop invariant: if no room left in current 982 * buffer, try to get some more. 983 */ 984 if (m_room == 0) { 985 size_t size = mp - mem; 986 char *nmem; 987 988 if ((nmem = realloc(mem, size + SFRAG)) == NULL) { 989 free(mem); 990 return (-2); 991 } 992 mem = nmem; 993 m_room = SFRAG; 994 mp = mem + size; 995 } 996 } 997 *mp++ = '\0'; /* loop invariant let's us do this */ 998 m_room--; 999 len = mp - mem - 1; 1000 1001 /* 1002 * Give back any extra memory and return value and success. 1003 */ 1004 if (m_room != 0) { 1005 char *nmem; 1006 1007 if ((nmem = realloc(mem, mp - mem)) == NULL) { 1008 free(mem); 1009 return (-2); 1010 } 1011 mem = nmem; 1012 } 1013 *str = mem; 1014 return (len); 1015} 1016DEF_WEAK(cgetustr); 1017 1018/* 1019 * Cgetnum retrieves the value of the numeric capability cap from the 1020 * capability record pointed to by buf. The numeric value is returned in 1021 * the long pointed to by num. 0 is returned on success, -1 if the requested 1022 * numeric capability couldn't be found. 1023 */ 1024int 1025cgetnum(char *buf, const char *cap, long *num) 1026{ 1027 long n; 1028 int base, digit; 1029 char *bp; 1030 1031 /* 1032 * Find numeric capability cap 1033 */ 1034 bp = cgetcap(buf, cap, '#'); 1035 if (bp == NULL) 1036 return (-1); 1037 1038 /* 1039 * Look at value and determine numeric base: 1040 * 0x... or 0X... hexadecimal, 1041 * else 0... octal, 1042 * else decimal. 1043 */ 1044 if (*bp == '0') { 1045 bp++; 1046 if (*bp == 'x' || *bp == 'X') { 1047 bp++; 1048 base = 16; 1049 } else 1050 base = 8; 1051 } else 1052 base = 10; 1053 1054 /* 1055 * Conversion loop ... 1056 */ 1057 n = 0; 1058 for (;;) { 1059 if ('0' <= *bp && *bp <= '9') 1060 digit = *bp - '0'; 1061 else if ('a' <= *bp && *bp <= 'f') 1062 digit = 10 + *bp - 'a'; 1063 else if ('A' <= *bp && *bp <= 'F') 1064 digit = 10 + *bp - 'A'; 1065 else 1066 break; 1067 1068 if (digit >= base) 1069 break; 1070 1071 n = n * base + digit; 1072 bp++; 1073 } 1074 1075 /* 1076 * Return value and success. 1077 */ 1078 *num = n; 1079 return (0); 1080} 1081DEF_WEAK(cgetnum); 1082 1083/* 1084 * Compare name field of record. 1085 */ 1086static int 1087nfcmp(const char *nf, char *rec) 1088{ 1089 char *cp, tmp; 1090 int ret; 1091 1092 for (cp = rec; *cp != ':'; cp++) 1093 ; 1094 1095 tmp = *(cp + 1); 1096 *(cp + 1) = '\0'; 1097 ret = strcmp(nf, rec); 1098 *(cp + 1) = tmp; 1099 1100 return (ret); 1101} 1102