1 /*- 2 * Copyright (c) 1992 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Casey Leedom of Lawrence Livermore National Laboratory. 7 * 8 * %sccs.include.redist.c% 9 */ 10 11 #if defined(LIBC_SCCS) && !defined(lint) 12 static char sccsid[] = "@(#)getcap.c 5.1 (Berkeley) 08/06/92"; 13 #endif /* LIBC_SCCS and not lint */ 14 15 #include <sys/types.h> 16 17 #include <ctype.h> 18 #include <errno.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #define BFRAG 1024 27 #define BSIZE 80 28 #define ESC ('[' & 037) /* ASCII ESC */ 29 #define MAX_RECURSION 32 /* maximum getent recursion */ 30 #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 31 32 static size_t topreclen; /* toprec length */ 33 static char *toprec; /* Additional record specified by cgetset() */ 34 35 static int getent __P((char **, u_int *, char **, int, char *, int)); 36 37 /* 38 * Cgetset() allows the addition of a user specified buffer to be added 39 * to the database array, in effect "pushing" the buffer on top of the 40 * virtual database. 0 is returned on success, -1 on failure. 41 */ 42 int 43 cgetset(ent) 44 char *ent; 45 { 46 if (ent == NULL) { 47 if (toprec) 48 free(toprec); 49 toprec = NULL; 50 topreclen = 0; 51 return (0); 52 } 53 topreclen = strlen(ent); 54 if ((toprec = malloc (topreclen + 1)) == NULL) { 55 errno = ENOMEM; 56 return (-1); 57 } 58 (void)strcpy(toprec, ent); 59 return (0); 60 } 61 62 /* 63 * Cgetcap searches the capability record buf for the capability cap with 64 * type `type'. A pointer to the value of cap is returned on success, NULL 65 * if the requested capability couldn't be found. 66 * 67 * Specifying a type of ':' means that nothing should follow cap (:cap:). 68 * In this case a pointer to the terminating ':' or NUL will be returned if 69 * cap is found. 70 * 71 * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 72 * return NULL. 73 */ 74 char * 75 cgetcap(buf, cap, type) 76 char *buf, *cap; 77 int type; 78 { 79 register char *bp, *cp; 80 81 bp = buf; 82 for (;;) { 83 /* 84 * Skip past the current capability field - it's either the 85 * name field if this is the first time through the loop, or 86 * the remainder of a field whose name failed to match cap. 87 */ 88 for (;;) 89 if (*bp == '\0') 90 return (NULL); 91 else 92 if (*bp++ == ':') 93 break; 94 95 /* 96 * Try to match (cap, type) in buf. 97 */ 98 for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 99 continue; 100 if (*cp != '\0') 101 continue; 102 if (*bp == '@') 103 return (NULL); 104 if (type == ':') { 105 if (*bp != '\0' && *bp != ':') 106 continue; 107 return(bp); 108 } 109 if (*bp != type) 110 continue; 111 bp++; 112 return (*bp == '@' ? NULL : bp); 113 } 114 /* NOTREACHED */ 115 } 116 117 /* 118 * Cgetent extracts the capability record name from the NULL terminated file 119 * array db_array and returns a pointer to a malloc'd copy of it in buf. 120 * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 121 * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 122 * -1 if the requested record couldn't be found, -2 if a system error was 123 * encountered (couldn't open/read a file, etc.), and -3 if a potential 124 * reference loop is detected. 125 */ 126 int 127 cgetent(buf, db_array, name) 128 char **buf, **db_array, *name; 129 { 130 u_int dummy; 131 132 return (getent(buf, &dummy, db_array, -1, name, 0)); 133 } 134 135 /* 136 * Getent implements the functions of cgetent. If fd is non-negative, 137 * *db_array has already been opened and fd is the open file descriptor. We 138 * do this to save time and avoid using up file descriptors for tc= 139 * recursions. 140 * 141 * Getent returns the same success/failure codes as cgetent. On success, a 142 * pointer to a malloc'ed capability record with all tc= capabilities fully 143 * expanded and its length (not including trailing ASCII NUL) are left in 144 * *cap and *len. 145 * 146 * Basic algorithm: 147 * + Allocate memory incrementally as needed in chunks of size BFRAG 148 * for capability buffer. 149 * + Recurse for each tc=name and interpolate result. Stop when all 150 * names interpolated, a name can't be found, or depth exceeds 151 * MAX_RECURSION. 152 */ 153 static int 154 getent(cap, len, db_array, fd, name, depth) 155 char **cap, **db_array, *name; 156 u_int *len; 157 int fd, depth; 158 { 159 register char *r_end, *rp, **db_p; 160 int myfd, eof, foundit; 161 char *record; 162 163 /* 164 * Return with ``loop detected'' error if we've recursed more than 165 * MAX_RECURSION times. 166 */ 167 if (depth > MAX_RECURSION) 168 return (-3); 169 170 /* 171 * Check if we have a top record from cgetset(). 172 */ 173 if (depth == 0 && toprec != NULL) { 174 if ((record = malloc (topreclen + BFRAG)) == NULL) { 175 errno = ENOMEM; 176 return (-2); 177 } 178 (void)strcpy(record, toprec); 179 myfd = 0; 180 db_p = db_array; 181 rp = record + topreclen + 1; 182 r_end = rp + BFRAG; 183 goto tc_exp; 184 } 185 /* 186 * Allocate first chunk of memory. 187 */ 188 if ((record = malloc(BFRAG)) == NULL) { 189 errno = ENOMEM; 190 return (-2); 191 } 192 r_end = record + BFRAG; 193 foundit = 0; 194 195 /* 196 * Loop through database array until finding the record. 197 */ 198 199 for (db_p = db_array; *db_p != NULL; db_p++) { 200 eof = 0; 201 202 /* 203 * Open database if not already open. 204 */ 205 if (fd >= 0) { 206 (void)lseek(fd, 0L, L_SET); 207 myfd = 0; 208 } else { 209 fd = open(*db_p, O_RDONLY, 0); 210 if (fd < 0) { 211 free(record); 212 return (-2); 213 } 214 myfd = 1; 215 } 216 217 /* 218 * Find the requested capability record ... 219 */ 220 { 221 char buf[BUFSIZ]; 222 register char *b_end, *bp; 223 register int c; 224 225 /* 226 * Loop invariants: 227 * There is always room for one more character in record. 228 * R_end always points just past end of record. 229 * Rp always points just past last character in record. 230 * B_end always points just past last character in buf. 231 * Bp always points at next character in buf. 232 */ 233 b_end = buf; 234 bp = buf; 235 for (;;) { 236 237 /* 238 * Read in a line implementing (\, newline) 239 * line continuation. 240 */ 241 rp = record; 242 for (;;) { 243 if (bp >= b_end) { 244 int n; 245 246 n = read(fd, buf, sizeof(buf)); 247 if (n <= 0) { 248 if (myfd) 249 (void)close(fd); 250 if (n < 0) { 251 free(record); 252 return (-2); 253 } else { 254 fd = -1; 255 eof = 1; 256 break; 257 } 258 } 259 b_end = buf+n; 260 bp = buf; 261 } 262 263 c = *bp++; 264 if (c == '\n') { 265 if (rp > record && *(rp-1) == '\\') { 266 rp--; 267 continue; 268 } else 269 break; 270 } 271 *rp++ = c; 272 273 /* 274 * Enforce loop invariant: if no room 275 * left in record buffer, try to get 276 * some more. 277 */ 278 if (rp >= r_end) { 279 u_int pos, newsize; 280 281 pos = rp - record; 282 newsize = r_end - record + BFRAG; 283 record = realloc(record, newsize); 284 if (record == NULL) { 285 errno = ENOMEM; 286 if (myfd) 287 (void)close(fd); 288 return (-2); 289 } 290 r_end = record + newsize; 291 rp = record + pos; 292 } 293 } 294 /* loop invariant let's us do this */ 295 *rp++ = '\0'; 296 297 /* 298 * If encountered eof check next file. 299 */ 300 if (eof) 301 break; 302 303 /* 304 * Toss blank lines and comments. 305 */ 306 if (*record == '\0' || *record == '#') 307 continue; 308 309 /* 310 * See if this is the record we want ... 311 */ 312 if (cgetmatch(record, name) == 0) { 313 foundit = 1; 314 break; /* found it! */ 315 } 316 } 317 } 318 if (foundit) 319 break; 320 } 321 322 if (!foundit) 323 return (-1); 324 325 /* 326 * Got the capability record, but now we have to expand all tc=name 327 * references in it ... 328 */ 329 tc_exp: { 330 register char *newicap, *s; 331 register int newilen; 332 u_int ilen; 333 int diff, iret, tclen; 334 char *icap, *scan, *tc, *tcstart, *tcend; 335 336 /* 337 * Loop invariants: 338 * There is room for one more character in record. 339 * R_end points just past end of record. 340 * Rp points just past last character in record. 341 * Scan points at remainder of record that needs to be 342 * scanned for tc=name constructs. 343 */ 344 scan = record; 345 for (;;) { 346 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 347 break; 348 349 /* 350 * Find end of tc=name and stomp on the trailing `:' 351 * (if present) so we can use it to call ourselves. 352 */ 353 s = tc; 354 for (;;) 355 if (*s == '\0') 356 break; 357 else 358 if (*s++ == ':') { 359 *(s-1) = '\0'; 360 break; 361 } 362 tcstart = tc - 3; 363 tclen = s - tcstart; 364 tcend = s; 365 366 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1); 367 newicap = icap; /* Put into a register. */ 368 newilen = ilen; 369 if (iret != 0) { 370 /* an error or couldn't resolve tc= */ 371 if (myfd) 372 (void)close(fd); 373 free(record); 374 return (iret); 375 } 376 377 /* not interested in name field of tc'ed record */ 378 s = newicap; 379 for (;;) 380 if (*s == '\0') 381 break; 382 else 383 if (*s++ == ':') 384 break; 385 newilen -= s - newicap; 386 newicap = s; 387 388 /* make sure interpolated record is `:'-terminated */ 389 s += newilen; 390 if (*(s-1) != ':') { 391 *s = ':'; /* overwrite NUL with : */ 392 newilen++; 393 } 394 395 /* 396 * Make sure there's enough room to insert the 397 * new record. 398 */ 399 diff = newilen - tclen; 400 if (diff >= r_end - rp) { 401 u_int pos, newsize, tcpos, tcposend; 402 403 pos = rp - record; 404 newsize = r_end - record + diff + BFRAG; 405 tcpos = tcstart - record; 406 tcposend = tcend - record; 407 record = realloc(record, newsize); 408 if (record == NULL) { 409 errno = ENOMEM; 410 if (myfd) 411 (void)close(fd); 412 free(icap); 413 return (-2); 414 } 415 r_end = record + newsize; 416 rp = record + pos; 417 tcstart = record + tcpos; 418 tcend = record + tcposend; 419 } 420 421 /* 422 * Insert tc'ed record into our record. 423 */ 424 s = tcstart + newilen; 425 bcopy(tcend, s, rp - tcend); 426 bcopy(newicap, tcstart, newilen); 427 rp += diff; 428 free(icap); 429 430 /* 431 * Start scan on `:' so next cgetcap works properly 432 * (cgetcap always skips first field). 433 */ 434 scan = s-1; 435 } 436 } 437 438 /* 439 * Close file (if we opened it), give back any extra memory, and 440 * return capability, length and success. 441 */ 442 if (myfd) 443 (void)close(fd); 444 *len = rp - record - 1; /* don't count NUL */ 445 if (r_end > rp) 446 record = realloc(record, (u_int)(rp - record)); 447 *cap = record; 448 return (0); 449 } 450 451 /* 452 * Cgetmatch will return 0 if name is one of the names of the capability 453 * record buf, -1 if not. 454 */ 455 int 456 cgetmatch(buf, name) 457 char *buf, *name; 458 { 459 register char *np, *bp; 460 461 /* 462 * Start search at beginning of record. 463 */ 464 bp = buf; 465 for (;;) { 466 /* 467 * Try to match a record name. 468 */ 469 np = name; 470 for (;;) 471 if (*np == '\0') 472 if (*bp == '|' || *bp == ':' || *bp == '\0') 473 return (0); 474 else 475 break; 476 else 477 if (*bp++ != *np++) 478 break; 479 480 /* 481 * Match failed, skip to next name in record. 482 */ 483 bp--; /* a '|' or ':' may have stopped the match */ 484 for (;;) 485 if (*bp == '\0' || *bp == ':') 486 return (-1); /* match failed totally */ 487 else 488 if (*bp++ == '|') 489 break; /* found next name */ 490 } 491 } 492 493 int 494 cgetfirst(buf, db_array) 495 char **buf, **db_array; 496 { 497 (void)cgetclose(); 498 return (cgetnext(buf, db_array)); 499 } 500 501 static FILE *pfp; 502 static int slash; 503 static char **dbp; 504 505 int 506 cgetclose() 507 { 508 if (pfp != NULL) { 509 (void)fclose(pfp); 510 pfp = NULL; 511 } 512 dbp = NULL; 513 slash = 0; 514 return (0); 515 } 516 517 /* 518 * Cgetnext() gets either the first or next entry in the logical database 519 * specified by db_array. It returns 0 upon completion of the database, 1 520 * upon returning an entry with more remaining, and -1 if an error occurs. 521 */ 522 int 523 cgetnext(bp, db_array) 524 register char **bp; 525 char **db_array; 526 { 527 size_t len; 528 int status; 529 char *cp, *line, *rp, buf[BSIZE]; 530 531 if (dbp == NULL) 532 dbp = db_array; 533 534 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) 535 return (-1); 536 537 for(;;) { 538 line = fgetline(pfp, &len); 539 if (line == NULL) { 540 (void)fclose(pfp); 541 if (ferror(pfp)) { 542 pfp = NULL; 543 dbp = NULL; 544 slash = 0; 545 return (-1); 546 } else { 547 dbp++; 548 if (*dbp == NULL) { 549 pfp = NULL; 550 dbp = NULL; 551 slash = 0; 552 return (0); 553 } else if ((pfp = fopen(*dbp, "r")) == NULL) { 554 pfp = NULL; 555 dbp = NULL; 556 slash = 0; 557 return (-1); 558 } else 559 continue; 560 } 561 } 562 if (isspace(*line) || *line == ':' || *line == '#' 563 || len == 0 || slash) { 564 if (len > 0 && line[len - 1] == '\\') 565 slash = 1; 566 else 567 slash = 0; 568 continue; 569 } 570 if (len > 0 && line[len - 1] == '\\') 571 slash = 1; 572 else 573 slash = 0; 574 575 /* line points to a name line */ 576 577 rp = buf; 578 for(cp = line; *cp != NULL; cp++) 579 if (*cp == '|' || *cp == ':') 580 break; 581 else 582 *rp++ = *cp; 583 584 *rp = '\0'; 585 status = cgetent(bp, db_array, &buf[0]); 586 if (status == 0) 587 return (1); 588 if (status == -2 || status == -3) { 589 pfp = NULL; 590 dbp = NULL; 591 slash = 0; 592 return (status + 1); 593 } 594 } 595 /* NOTREACHED */ 596 } 597 598 /* 599 * Cgetstr retrieves the value of the string capability cap from the 600 * capability record pointed to by buf. A pointer to a decoded, NUL 601 * terminated, malloc'd copy of the string is returned in the char * 602 * pointed to by str. The length of the string not including the trailing 603 * NUL is returned on success, -1 if the requested string capability 604 * couldn't be found, -2 if a system error was encountered (storage 605 * allocation failure). 606 */ 607 int 608 cgetstr(buf, cap, str) 609 char *buf, *cap; 610 char **str; 611 { 612 register u_int m_room; 613 register char *bp, *mp; 614 int len; 615 char *mem; 616 617 /* 618 * Find string capability cap 619 */ 620 bp = cgetcap(buf, cap, '='); 621 if (bp == NULL) 622 return (-1); 623 624 /* 625 * Conversion / storage allocation loop ... Allocate memory in 626 * chunks SFRAG in size. 627 */ 628 if ((mem = malloc(SFRAG)) == NULL) { 629 errno = ENOMEM; 630 return (-2); /* couldn't even allocate the first fragment */ 631 } 632 m_room = SFRAG; 633 mp = mem; 634 635 while (*bp != ':' && *bp != '\0') { 636 /* 637 * Loop invariants: 638 * There is always room for one more character in mem. 639 * Mp always points just past last character in mem. 640 * Bp always points at next character in buf. 641 */ 642 if (*bp == '^') { 643 bp++; 644 if (*bp == ':' || *bp == '\0') 645 break; /* drop unfinished escape */ 646 *mp++ = *bp++ & 037; 647 } else if (*bp == '\\') { 648 bp++; 649 if (*bp == ':' || *bp == '\0') 650 break; /* drop unfinished escape */ 651 if ('0' <= *bp && *bp <= '7') { 652 register int n, i; 653 654 n = 0; 655 i = 3; /* maximum of three octal digits */ 656 do { 657 n = n * 8 + (*bp++ - '0'); 658 } while (--i && '0' <= *bp && *bp <= '7'); 659 *mp++ = n; 660 } 661 else switch (*bp++) { 662 case 'b': case 'B': 663 *mp++ = '\b'; 664 break; 665 case 't': case 'T': 666 *mp++ = '\t'; 667 break; 668 case 'n': case 'N': 669 *mp++ = '\n'; 670 break; 671 case 'f': case 'F': 672 *mp++ = '\f'; 673 break; 674 case 'r': case 'R': 675 *mp++ = '\r'; 676 break; 677 case 'e': case 'E': 678 *mp++ = ESC; 679 break; 680 case 'c': case 'C': 681 *mp++ = ':'; 682 break; 683 default: 684 /* 685 * Catches '\', '^', and 686 * everything else. 687 */ 688 *mp++ = *(bp-1); 689 break; 690 } 691 } else 692 *mp++ = *bp++; 693 m_room--; 694 695 /* 696 * Enforce loop invariant: if no room left in current 697 * buffer, try to get some more. 698 */ 699 if (m_room == 0) { 700 u_int size = mp - mem; 701 702 if ((mem = realloc(mem, size + SFRAG)) == NULL) 703 return (-2); 704 m_room = SFRAG; 705 mp = mem + size; 706 } 707 } 708 *mp++ = '\0'; /* loop invariant let's us do this */ 709 m_room--; 710 len = mp - mem - 1; 711 712 /* 713 * Give back any extra memory and return value and success. 714 */ 715 if (m_room != 0) 716 mem = realloc(mem, (u_int)(mp - mem)); 717 *str = mem; 718 return (len); 719 } 720 721 /* 722 * Cgetustr retrieves the value of the string capability cap from the 723 * capability record pointed to by buf. The difference between cgetustr() 724 * and cgetstr() is that cgetustr does not decode escapes but rather treats 725 * all characters literally. A pointer to a NUL terminated malloc'd 726 * copy of the string is returned in the char pointed to by str. The 727 * length of the string not including the trailing NUL is returned on success, 728 * -1 if the requested string capability couldn't be found, -2 if a system 729 * error was encountered (storage allocation failure). 730 */ 731 int 732 cgetustr(buf, cap, str) 733 char *buf, *cap, **str; 734 { 735 register u_int m_room; 736 register char *bp, *mp; 737 int len; 738 char *mem; 739 740 /* 741 * Find string capability cap 742 */ 743 if ((bp = cgetcap(buf, cap, '=')) == NULL) 744 return (-1); 745 746 /* 747 * Conversion / storage allocation loop ... Allocate memory in 748 * chunks SFRAG in size. 749 */ 750 if ((mem = malloc(SFRAG)) == NULL) { 751 errno = ENOMEM; 752 return (-2); /* couldn't even allocate the first fragment */ 753 } 754 m_room = SFRAG; 755 mp = mem; 756 757 while (*bp != ':' && *bp != '\0') { 758 /* 759 * Loop invariants: 760 * There is always room for one more character in mem. 761 * Mp always points just past last character in mem. 762 * Bp always points at next character in buf. 763 */ 764 *mp++ = *bp++; 765 m_room--; 766 767 /* 768 * Enforce loop invariant: if no room left in current 769 * buffer, try to get some more. 770 */ 771 if (m_room == 0) { 772 u_int size = mp - mem; 773 774 if ((mem = realloc(mem, size + SFRAG)) == NULL) 775 return (-2); 776 m_room = SFRAG; 777 mp = mem + size; 778 } 779 } 780 *mp++ = '\0'; /* loop invariant let's us do this */ 781 m_room--; 782 len = mp - mem - 1; 783 784 /* 785 * Give back any extra memory and return value and success. 786 */ 787 if (m_room != 0) 788 mem = realloc(mem, (u_int)(mp - mem)); 789 *str = mem; 790 return (len); 791 } 792 793 /* 794 * Cgetnum retrieves the value of the numeric capability cap from the 795 * capability record pointed to by buf. The numeric value is returned in 796 * the long pointed to by num. 0 is returned on success, -1 if the requested 797 * numeric capability couldn't be found. 798 */ 799 int 800 cgetnum(buf, cap, num) 801 char *buf, *cap; 802 long *num; 803 { 804 register long n; 805 register int base, digit; 806 register char *bp; 807 808 /* 809 * Find numeric capability cap 810 */ 811 bp = cgetcap(buf, cap, '#'); 812 if (bp == NULL) 813 return (-1); 814 815 /* 816 * Look at value and determine numeric base: 817 * 0x... or 0X... hexadecimal, 818 * else 0... octal, 819 * else decimal. 820 */ 821 if (*bp == '0') { 822 bp++; 823 if (*bp == 'x' || *bp == 'X') { 824 bp++; 825 base = 16; 826 } else 827 base = 8; 828 } else 829 base = 10; 830 831 /* 832 * Conversion loop ... 833 */ 834 n = 0; 835 for (;;) { 836 if ('0' <= *bp && *bp <= '9') 837 digit = *bp - '0'; 838 else 839 if ( ('a' <= *bp && *bp <= 'f') 840 || ('A' <= *bp && *bp <= 'F')) 841 digit = 10 + *bp - 'a'; 842 else 843 break; 844 845 if (digit >= base) 846 break; 847 848 n = n * base + digit; 849 bp++; 850 } 851 852 /* 853 * Return value and success. 854 */ 855 *num = n; 856 return (0); 857 } 858