getcap.c revision 90039
1193323Sed/*- 2193323Sed * Copyright (c) 1992, 1993 3193323Sed * The Regents of the University of California. All rights reserved. 4193323Sed * 5193323Sed * This code is derived from software contributed to Berkeley by 6193323Sed * Casey Leedom of Lawrence Livermore National Laboratory. 7193323Sed * 8193323Sed * Redistribution and use in source and binary forms, with or without 9193323Sed * modification, are permitted provided that the following conditions 10193323Sed * are met: 11193323Sed * 1. Redistributions of source code must retain the above copyright 12193323Sed * notice, this list of conditions and the following disclaimer. 13193323Sed * 2. Redistributions in binary form must reproduce the above copyright 14193323Sed * notice, this list of conditions and the following disclaimer in the 15193323Sed * documentation and/or other materials provided with the distribution. 16193323Sed * 3. All advertising materials mentioning features or use of this software 17193323Sed * must display the following acknowledgement: 18193323Sed * This product includes software developed by the University of 19193323Sed * California, Berkeley and its contributors. 20193323Sed * 4. Neither the name of the University nor the names of its contributors 21193323Sed * may be used to endorse or promote products derived from this software 22199481Srdivacky * without specific prior written permission. 23193323Sed * 24193323Sed * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25194612Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30198090Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34193323Sed * SUCH DAMAGE. 35198090Srdivacky */ 36193323Sed 37193323Sed#if defined(LIBC_SCCS) && !defined(lint) 38193323Sedstatic char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; 39193323Sed#endif /* LIBC_SCCS and not lint */ 40193323Sed#include <sys/cdefs.h> 41198090Srdivacky__FBSDID("$FreeBSD: head/lib/libc/gen/getcap.c 90039 2002-02-01 00:57:29Z obrien $"); 42198090Srdivacky 43198090Srdivacky#include "namespace.h" 44198090Srdivacky#include <sys/types.h> 45193323Sed 46193323Sed#include <ctype.h> 47193323Sed#include <errno.h> 48193323Sed#include <fcntl.h> 49199481Srdivacky#include <limits.h> 50199481Srdivacky#include <stdio.h> 51199481Srdivacky#include <stdlib.h> 52199481Srdivacky#include <string.h> 53199481Srdivacky#include <unistd.h> 54199481Srdivacky#include "un-namespace.h" 55199481Srdivacky 56193323Sed#include <db.h> 57198090Srdivacky 58198090Srdivacky#define BFRAG 1024 59193323Sed#define BSIZE 1024 60193323Sed#define ESC ('[' & 037) /* ASCII ESC */ 61193323Sed#define MAX_RECURSION 32 /* maximum getent recursion */ 62193323Sed#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 63193323Sed 64193323Sed#define RECOK (char)0 65193323Sed#define TCERR (char)1 66193323Sed#define SHADOW (char)2 67193323Sed 68193323Sedstatic size_t topreclen; /* toprec length */ 69193323Sedstatic char *toprec; /* Additional record specified by cgetset() */ 70193323Sedstatic int gottoprec; /* Flag indicating retrieval of toprecord */ 71193323Sed 72193323Sedstatic int cdbget(DB *, char **, char *); 73193323Sedstatic int getent(char **, u_int *, char **, int, char *, int, char *); 74193323Sedstatic int nfcmp(char *, char *); 75193323Sed 76193323Sed/* 77193323Sed * Cgetset() allows the addition of a user specified buffer to be added 78193323Sed * to the database array, in effect "pushing" the buffer on top of the 79193323Sed * virtual database. 0 is returned on success, -1 on failure. 80193323Sed */ 81193323Sedint 82193323Sedcgetset(ent) 83193323Sed char *ent; 84193323Sed{ 85193323Sed if (ent == NULL) { 86193323Sed if (toprec) 87193323Sed free(toprec); 88193323Sed toprec = NULL; 89193323Sed topreclen = 0; 90193323Sed return (0); 91193323Sed } 92193323Sed topreclen = strlen(ent); 93193323Sed if ((toprec = malloc (topreclen + 1)) == NULL) { 94193323Sed errno = ENOMEM; 95193323Sed return (-1); 96193323Sed } 97193323Sed gottoprec = 0; 98193323Sed (void)strcpy(toprec, ent); 99193323Sed return (0); 100193323Sed} 101193323Sed 102193323Sed/* 103193323Sed * Cgetcap searches the capability record buf for the capability cap with 104193323Sed * type `type'. A pointer to the value of cap is returned on success, NULL 105193323Sed * if the requested capability couldn't be found. 106193323Sed * 107193323Sed * Specifying a type of ':' means that nothing should follow cap (:cap:). 108193323Sed * In this case a pointer to the terminating ':' or NUL will be returned if 109193323Sed * cap is found. 110193323Sed * 111193323Sed * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 112193323Sed * return NULL. 113193323Sed */ 114193323Sedchar * 115193323Sedcgetcap(buf, cap, type) 116193323Sed char *buf, *cap; 117193323Sed int type; 118199481Srdivacky{ 119199481Srdivacky char *bp, *cp; 120199481Srdivacky 121199481Srdivacky bp = buf; 122199481Srdivacky for (;;) { 123199481Srdivacky /* 124199481Srdivacky * Skip past the current capability field - it's either the 125199481Srdivacky * name field if this is the first time through the loop, or 126199481Srdivacky * the remainder of a field whose name failed to match cap. 127199481Srdivacky */ 128199481Srdivacky for (;;) 129199481Srdivacky if (*bp == '\0') 130199481Srdivacky return (NULL); 131199481Srdivacky else 132199481Srdivacky if (*bp++ == ':') 133199481Srdivacky break; 134193323Sed 135193323Sed /* 136194612Sed * Try to match (cap, type) in buf. 137193323Sed */ 138193323Sed for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 139193323Sed continue; 140193323Sed if (*cp != '\0') 141193323Sed continue; 142193323Sed if (*bp == '@') 143193323Sed return (NULL); 144193323Sed if (type == ':') { 145193323Sed if (*bp != '\0' && *bp != ':') 146193323Sed continue; 147193323Sed return(bp); 148193323Sed } 149193323Sed if (*bp != type) 150193323Sed continue; 151193323Sed bp++; 152193323Sed return (*bp == '@' ? NULL : bp); 153193323Sed } 154193323Sed /* NOTREACHED */ 155193323Sed} 156193323Sed 157193323Sed/* 158193323Sed * Cgetent extracts the capability record name from the NULL terminated file 159193323Sed * array db_array and returns a pointer to a malloc'd copy of it in buf. 160193323Sed * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 161193323Sed * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 162193323Sed * -1 if the requested record couldn't be found, -2 if a system error was 163193323Sed * encountered (couldn't open/read a file, etc.), and -3 if a potential 164198090Srdivacky * reference loop is detected. 165198090Srdivacky */ 166198090Srdivackyint 167193323Sedcgetent(buf, db_array, name) 168193323Sed char **buf, **db_array, *name; 169193323Sed{ 170193323Sed u_int dummy; 171198090Srdivacky 172193323Sed return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 173193323Sed} 174193323Sed 175193323Sed/* 176193323Sed * Getent implements the functions of cgetent. If fd is non-negative, 177193323Sed * *db_array has already been opened and fd is the open file descriptor. We 178193323Sed * do this to save time and avoid using up file descriptors for tc= 179193323Sed * recursions. 180193323Sed * 181193323Sed * Getent returns the same success/failure codes as cgetent. On success, a 182193323Sed * pointer to a malloc'ed capability record with all tc= capabilities fully 183193323Sed * expanded and its length (not including trailing ASCII NUL) are left in 184193323Sed * *cap and *len. 185193323Sed * 186193323Sed * Basic algorithm: 187193323Sed * + Allocate memory incrementally as needed in chunks of size BFRAG 188193323Sed * for capability buffer. 189193323Sed * + Recurse for each tc=name and interpolate result. Stop when all 190193323Sed * names interpolated, a name can't be found, or depth exceeds 191193323Sed * MAX_RECURSION. 192193323Sed */ 193193323Sedstatic int 194193323Sedgetent(cap, len, db_array, fd, name, depth, nfield) 195193323Sed char **cap, **db_array, *name, *nfield; 196193323Sed u_int *len; 197193323Sed int fd, depth; 198193323Sed{ 199193323Sed DB *capdbp; 200193323Sed char *r_end, *rp, **db_p; 201193323Sed int myfd, eof, foundit, retval, clen; 202193323Sed char *record, *cbuf; 203193323Sed int tc_not_resolved; 204193323Sed char pbuf[_POSIX_PATH_MAX]; 205193323Sed 206193323Sed /* 207193323Sed * Return with ``loop detected'' error if we've recursed more than 208193323Sed * MAX_RECURSION times. 209193323Sed */ 210193323Sed if (depth > MAX_RECURSION) 211193323Sed return (-3); 212193323Sed 213193323Sed /* 214193323Sed * Check if we have a top record from cgetset(). 215193323Sed */ 216193323Sed if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 217193323Sed if ((record = malloc (topreclen + BFRAG)) == NULL) { 218193323Sed errno = ENOMEM; 219193323Sed return (-2); 220193323Sed } 221193323Sed (void)strcpy(record, toprec); 222193323Sed myfd = 0; 223193323Sed db_p = db_array; 224193323Sed rp = record + topreclen + 1; 225193323Sed r_end = rp + BFRAG; 226193323Sed goto tc_exp; 227193323Sed } 228193323Sed /* 229193323Sed * Allocate first chunk of memory. 230193323Sed */ 231193323Sed if ((record = malloc(BFRAG)) == NULL) { 232193323Sed errno = ENOMEM; 233198090Srdivacky return (-2); 234193323Sed } 235193323Sed r_end = record + BFRAG; 236193323Sed foundit = 0; 237193323Sed /* 238193323Sed * Loop through database array until finding the record. 239193323Sed */ 240193323Sed 241193323Sed for (db_p = db_array; *db_p != NULL; db_p++) { 242193323Sed eof = 0; 243193323Sed 244193323Sed /* 245193323Sed * Open database if not already open. 246193323Sed */ 247193323Sed 248193323Sed if (fd >= 0) { 249193323Sed (void)lseek(fd, (off_t)0, SEEK_SET); 250193323Sed myfd = 0; 251193323Sed } else { 252193323Sed (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 253193323Sed if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 254193323Sed != NULL) { 255193323Sed free(record); 256193323Sed retval = cdbget(capdbp, &record, name); 257193323Sed if (retval < 0) { 258193323Sed /* no record available */ 259193323Sed (void)capdbp->close(capdbp); 260193323Sed return (retval); 261193323Sed } 262193323Sed /* save the data; close frees it */ 263193323Sed clen = strlen(record); 264193323Sed cbuf = malloc(clen + 1); 265193323Sed memcpy(cbuf, record, clen + 1); 266193323Sed if (capdbp->close(capdbp) < 0) { 267193323Sed free(cbuf); 268193323Sed return (-2); 269193323Sed } 270193323Sed *len = clen; 271193323Sed *cap = cbuf; 272193323Sed return (retval); 273193323Sed } else { 274193323Sed fd = _open(*db_p, O_RDONLY, 0); 275193323Sed if (fd < 0) 276193323Sed continue; 277193323Sed myfd = 1; 278193323Sed } 279193323Sed } 280193323Sed /* 281193323Sed * Find the requested capability record ... 282193323Sed */ 283193323Sed { 284193323Sed char buf[BUFSIZ]; 285193323Sed char *b_end, *bp; 286193323Sed int c; 287193323Sed 288193323Sed /* 289193323Sed * Loop invariants: 290193323Sed * There is always room for one more character in record. 291202375Srdivacky * R_end always points just past end of record. 292193323Sed * Rp always points just past last character in record. 293193323Sed * B_end always points just past last character in buf. 294193323Sed * Bp always points at next character in buf. 295193323Sed */ 296193323Sed b_end = buf; 297193323Sed bp = buf; 298193323Sed for (;;) { 299202375Srdivacky 300202375Srdivacky /* 301202375Srdivacky * Read in a line implementing (\, newline) 302202375Srdivacky * line continuation. 303202375Srdivacky */ 304193323Sed rp = record; 305193323Sed for (;;) { 306193323Sed if (bp >= b_end) { 307198090Srdivacky int n; 308198090Srdivacky 309193323Sed n = _read(fd, buf, sizeof(buf)); 310193323Sed if (n <= 0) { 311193323Sed if (myfd) 312198090Srdivacky (void)_close(fd); 313198090Srdivacky if (n < 0) { 314193323Sed free(record); 315193323Sed return (-2); 316193323Sed } else { 317193323Sed fd = -1; 318193323Sed eof = 1; 319193323Sed break; 320193323Sed } 321198090Srdivacky } 322198090Srdivacky b_end = buf+n; 323198090Srdivacky bp = buf; 324198090Srdivacky } 325198090Srdivacky 326198090Srdivacky c = *bp++; 327198090Srdivacky if (c == '\n') { 328200581Srdivacky if (rp > record && *(rp-1) == '\\') { 329200581Srdivacky rp--; 330200581Srdivacky continue; 331200581Srdivacky } else 332200581Srdivacky break; 333193323Sed } 334193323Sed *rp++ = c; 335193323Sed 336193323Sed /* 337193323Sed * Enforce loop invariant: if no room 338193323Sed * left in record buffer, try to get 339193323Sed * some more. 340193323Sed */ 341193323Sed if (rp >= r_end) { 342193323Sed u_int pos; 343193323Sed size_t newsize; 344193323Sed 345193323Sed pos = rp - record; 346193323Sed newsize = r_end - record + BFRAG; 347193323Sed record = reallocf(record, newsize); 348193323Sed if (record == NULL) { 349193323Sed errno = ENOMEM; 350193323Sed if (myfd) 351193323Sed (void)_close(fd); 352193323Sed return (-2); 353193323Sed } 354193323Sed r_end = record + newsize; 355193323Sed rp = record + pos; 356193323Sed } 357193323Sed } 358193323Sed /* loop invariant let's us do this */ 359193323Sed *rp++ = '\0'; 360193323Sed 361193323Sed /* 362193323Sed * If encountered eof check next file. 363198090Srdivacky */ 364198090Srdivacky if (eof) 365198090Srdivacky break; 366198090Srdivacky 367193323Sed /* 368198090Srdivacky * Toss blank lines and comments. 369198090Srdivacky */ 370198090Srdivacky if (*record == '\0' || *record == '#') 371198090Srdivacky continue; 372198090Srdivacky 373198090Srdivacky /* 374193323Sed * See if this is the record we want ... 375193323Sed */ 376193323Sed if (cgetmatch(record, name) == 0) { 377193323Sed if (nfield == NULL || !nfcmp(nfield, record)) { 378193323Sed foundit = 1; 379193323Sed break; /* found it! */ 380193323Sed } 381193323Sed } 382193323Sed } 383193323Sed } 384193323Sed if (foundit) 385193323Sed break; 386193323Sed } 387193323Sed 388193323Sed if (!foundit) { 389193323Sed free(record); 390193323Sed return (-1); 391193323Sed } 392193323Sed 393193323Sed /* 394193323Sed * Got the capability record, but now we have to expand all tc=name 395193323Sed * references in it ... 396193323Sed */ 397193323Sedtc_exp: { 398193323Sed char *newicap, *s; 399193323Sed int newilen; 400193323Sed u_int ilen; 401193323Sed int diff, iret, tclen; 402193323Sed char *icap, *scan, *tc, *tcstart, *tcend; 403193323Sed 404193323Sed /* 405193323Sed * Loop invariants: 406193323Sed * There is room for one more character in record. 407 * R_end points just past end of record. 408 * Rp points just past last character in record. 409 * Scan points at remainder of record that needs to be 410 * scanned for tc=name constructs. 411 */ 412 scan = record; 413 tc_not_resolved = 0; 414 for (;;) { 415 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 416 break; 417 418 /* 419 * Find end of tc=name and stomp on the trailing `:' 420 * (if present) so we can use it to call ourselves. 421 */ 422 s = tc; 423 for (;;) 424 if (*s == '\0') 425 break; 426 else 427 if (*s++ == ':') { 428 *(s - 1) = '\0'; 429 break; 430 } 431 tcstart = tc - 3; 432 tclen = s - tcstart; 433 tcend = s; 434 435 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 436 NULL); 437 newicap = icap; /* Put into a register. */ 438 newilen = ilen; 439 if (iret != 0) { 440 /* an error */ 441 if (iret < -1) { 442 if (myfd) 443 (void)_close(fd); 444 free(record); 445 return (iret); 446 } 447 if (iret == 1) 448 tc_not_resolved = 1; 449 /* couldn't resolve tc */ 450 if (iret == -1) { 451 *(s - 1) = ':'; 452 scan = s - 1; 453 tc_not_resolved = 1; 454 continue; 455 456 } 457 } 458 /* not interested in name field of tc'ed record */ 459 s = newicap; 460 for (;;) 461 if (*s == '\0') 462 break; 463 else 464 if (*s++ == ':') 465 break; 466 newilen -= s - newicap; 467 newicap = s; 468 469 /* make sure interpolated record is `:'-terminated */ 470 s += newilen; 471 if (*(s-1) != ':') { 472 *s = ':'; /* overwrite NUL with : */ 473 newilen++; 474 } 475 476 /* 477 * Make sure there's enough room to insert the 478 * new record. 479 */ 480 diff = newilen - tclen; 481 if (diff >= r_end - rp) { 482 u_int pos, tcpos, tcposend; 483 size_t newsize; 484 485 pos = rp - record; 486 newsize = r_end - record + diff + BFRAG; 487 tcpos = tcstart - record; 488 tcposend = tcend - record; 489 record = reallocf(record, newsize); 490 if (record == NULL) { 491 errno = ENOMEM; 492 if (myfd) 493 (void)_close(fd); 494 free(icap); 495 return (-2); 496 } 497 r_end = record + newsize; 498 rp = record + pos; 499 tcstart = record + tcpos; 500 tcend = record + tcposend; 501 } 502 503 /* 504 * Insert tc'ed record into our record. 505 */ 506 s = tcstart + newilen; 507 bcopy(tcend, s, rp - tcend); 508 bcopy(newicap, tcstart, newilen); 509 rp += diff; 510 free(icap); 511 512 /* 513 * Start scan on `:' so next cgetcap works properly 514 * (cgetcap always skips first field). 515 */ 516 scan = s-1; 517 } 518 519 } 520 /* 521 * Close file (if we opened it), give back any extra memory, and 522 * return capability, length and success. 523 */ 524 if (myfd) 525 (void)_close(fd); 526 *len = rp - record - 1; /* don't count NUL */ 527 if (r_end > rp) 528 if ((record = 529 reallocf(record, (size_t)(rp - record))) == NULL) { 530 errno = ENOMEM; 531 return (-2); 532 } 533 534 *cap = record; 535 if (tc_not_resolved) 536 return (1); 537 return (0); 538} 539 540static int 541cdbget(capdbp, bp, name) 542 DB *capdbp; 543 char **bp, *name; 544{ 545 DBT key, data; 546 547 key.data = name; 548 key.size = strlen(name); 549 550 for (;;) { 551 /* Get the reference. */ 552 switch(capdbp->get(capdbp, &key, &data, 0)) { 553 case -1: 554 return (-2); 555 case 1: 556 return (-1); 557 } 558 559 /* If not an index to another record, leave. */ 560 if (((char *)data.data)[0] != SHADOW) 561 break; 562 563 key.data = (char *)data.data + 1; 564 key.size = data.size - 1; 565 } 566 567 *bp = (char *)data.data + 1; 568 return (((char *)(data.data))[0] == TCERR ? 1 : 0); 569} 570 571/* 572 * Cgetmatch will return 0 if name is one of the names of the capability 573 * record buf, -1 if not. 574 */ 575int 576cgetmatch(buf, name) 577 char *buf, *name; 578{ 579 char *np, *bp; 580 581 /* 582 * Start search at beginning of record. 583 */ 584 bp = buf; 585 for (;;) { 586 /* 587 * Try to match a record name. 588 */ 589 np = name; 590 for (;;) 591 if (*np == '\0') 592 if (*bp == '|' || *bp == ':' || *bp == '\0') 593 return (0); 594 else 595 break; 596 else 597 if (*bp++ != *np++) 598 break; 599 600 /* 601 * Match failed, skip to next name in record. 602 */ 603 bp--; /* a '|' or ':' may have stopped the match */ 604 for (;;) 605 if (*bp == '\0' || *bp == ':') 606 return (-1); /* match failed totally */ 607 else 608 if (*bp++ == '|') 609 break; /* found next name */ 610 } 611} 612 613 614 615 616 617int 618cgetfirst(buf, db_array) 619 char **buf, **db_array; 620{ 621 (void)cgetclose(); 622 return (cgetnext(buf, db_array)); 623} 624 625static FILE *pfp; 626static int slash; 627static char **dbp; 628 629int 630cgetclose() 631{ 632 if (pfp != NULL) { 633 (void)fclose(pfp); 634 pfp = NULL; 635 } 636 dbp = NULL; 637 gottoprec = 0; 638 slash = 0; 639 return(0); 640} 641 642/* 643 * Cgetnext() gets either the first or next entry in the logical database 644 * specified by db_array. It returns 0 upon completion of the database, 1 645 * upon returning an entry with more remaining, and -1 if an error occurs. 646 */ 647int 648cgetnext(bp, db_array) 649 char **bp; 650 char **db_array; 651{ 652 size_t len; 653 int done, hadreaderr, i, savederrno, status; 654 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 655 u_int dummy; 656 657 if (dbp == NULL) 658 dbp = db_array; 659 660 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 661 (void)cgetclose(); 662 return (-1); 663 } 664 for(;;) { 665 if (toprec && !gottoprec) { 666 gottoprec = 1; 667 line = toprec; 668 } else { 669 line = fgetln(pfp, &len); 670 if (line == NULL && pfp) { 671 hadreaderr = ferror(pfp); 672 if (hadreaderr) 673 savederrno = errno; 674 fclose(pfp); 675 pfp = NULL; 676 if (hadreaderr) { 677 cgetclose(); 678 errno = savederrno; 679 return (-1); 680 } else { 681 if (*++dbp == NULL) { 682 (void)cgetclose(); 683 return (0); 684 } else if ((pfp = 685 fopen(*dbp, "r")) == NULL) { 686 (void)cgetclose(); 687 return (-1); 688 } else 689 continue; 690 } 691 } else 692 line[len - 1] = '\0'; 693 if (len == 1) { 694 slash = 0; 695 continue; 696 } 697 if (isspace((unsigned char)*line) || 698 *line == ':' || *line == '#' || slash) { 699 if (line[len - 2] == '\\') 700 slash = 1; 701 else 702 slash = 0; 703 continue; 704 } 705 if (line[len - 2] == '\\') 706 slash = 1; 707 else 708 slash = 0; 709 } 710 711 712 /* 713 * Line points to a name line. 714 */ 715 i = 0; 716 done = 0; 717 np = nbuf; 718 for (;;) { 719 for (cp = line; *cp != '\0'; cp++) { 720 if (*cp == ':') { 721 *np++ = ':'; 722 done = 1; 723 break; 724 } 725 if (*cp == '\\') 726 break; 727 *np++ = *cp; 728 } 729 if (done) { 730 *np = '\0'; 731 break; 732 } else { /* name field extends beyond the line */ 733 line = fgetln(pfp, &len); 734 if (line == NULL && pfp) { 735 /* Name extends beyond the EOF! */ 736 hadreaderr = ferror(pfp); 737 if (hadreaderr) 738 savederrno = errno; 739 fclose(pfp); 740 pfp = NULL; 741 if (hadreaderr) { 742 cgetclose(); 743 errno = savederrno; 744 return (-1); 745 } else { 746 cgetclose(); 747 return (-1); 748 } 749 } else 750 line[len - 1] = '\0'; 751 } 752 } 753 rp = buf; 754 for(cp = nbuf; *cp != '\0'; cp++) 755 if (*cp == '|' || *cp == ':') 756 break; 757 else 758 *rp++ = *cp; 759 760 *rp = '\0'; 761 /* 762 * XXX 763 * Last argument of getent here should be nbuf if we want true 764 * sequential access in the case of duplicates. 765 * With NULL, getent will return the first entry found 766 * rather than the duplicate entry record. This is a 767 * matter of semantics that should be resolved. 768 */ 769 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 770 if (status == -2 || status == -3) 771 (void)cgetclose(); 772 773 return (status + 1); 774 } 775 /* NOTREACHED */ 776} 777 778/* 779 * Cgetstr retrieves the value of the string capability cap from the 780 * capability record pointed to by buf. A pointer to a decoded, NUL 781 * terminated, malloc'd copy of the string is returned in the char * 782 * pointed to by str. The length of the string not including the trailing 783 * NUL is returned on success, -1 if the requested string capability 784 * couldn't be found, -2 if a system error was encountered (storage 785 * allocation failure). 786 */ 787int 788cgetstr(buf, cap, str) 789 char *buf, *cap; 790 char **str; 791{ 792 u_int m_room; 793 char *bp, *mp; 794 int len; 795 char *mem; 796 797 /* 798 * Find string capability cap 799 */ 800 bp = cgetcap(buf, cap, '='); 801 if (bp == NULL) 802 return (-1); 803 804 /* 805 * Conversion / storage allocation loop ... Allocate memory in 806 * chunks SFRAG in size. 807 */ 808 if ((mem = malloc(SFRAG)) == NULL) { 809 errno = ENOMEM; 810 return (-2); /* couldn't even allocate the first fragment */ 811 } 812 m_room = SFRAG; 813 mp = mem; 814 815 while (*bp != ':' && *bp != '\0') { 816 /* 817 * Loop invariants: 818 * There is always room for one more character in mem. 819 * Mp always points just past last character in mem. 820 * Bp always points at next character in buf. 821 */ 822 if (*bp == '^') { 823 bp++; 824 if (*bp == ':' || *bp == '\0') 825 break; /* drop unfinished escape */ 826 if (*bp == '?') { 827 *mp++ = '\177'; 828 bp++; 829 } else 830 *mp++ = *bp++ & 037; 831 } else if (*bp == '\\') { 832 bp++; 833 if (*bp == ':' || *bp == '\0') 834 break; /* drop unfinished escape */ 835 if ('0' <= *bp && *bp <= '7') { 836 int n, i; 837 838 n = 0; 839 i = 3; /* maximum of three octal digits */ 840 do { 841 n = n * 8 + (*bp++ - '0'); 842 } while (--i && '0' <= *bp && *bp <= '7'); 843 *mp++ = n; 844 } 845 else switch (*bp++) { 846 case 'b': case 'B': 847 *mp++ = '\b'; 848 break; 849 case 't': case 'T': 850 *mp++ = '\t'; 851 break; 852 case 'n': case 'N': 853 *mp++ = '\n'; 854 break; 855 case 'f': case 'F': 856 *mp++ = '\f'; 857 break; 858 case 'r': case 'R': 859 *mp++ = '\r'; 860 break; 861 case 'e': case 'E': 862 *mp++ = ESC; 863 break; 864 case 'c': case 'C': 865 *mp++ = ':'; 866 break; 867 default: 868 /* 869 * Catches '\', '^', and 870 * everything else. 871 */ 872 *mp++ = *(bp-1); 873 break; 874 } 875 } else 876 *mp++ = *bp++; 877 m_room--; 878 879 /* 880 * Enforce loop invariant: if no room left in current 881 * buffer, try to get some more. 882 */ 883 if (m_room == 0) { 884 size_t size = mp - mem; 885 886 if ((mem = reallocf(mem, size + SFRAG)) == NULL) 887 return (-2); 888 m_room = SFRAG; 889 mp = mem + size; 890 } 891 } 892 *mp++ = '\0'; /* loop invariant let's us do this */ 893 m_room--; 894 len = mp - mem - 1; 895 896 /* 897 * Give back any extra memory and return value and success. 898 */ 899 if (m_room != 0) 900 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL) 901 return (-2); 902 *str = mem; 903 return (len); 904} 905 906/* 907 * Cgetustr retrieves the value of the string capability cap from the 908 * capability record pointed to by buf. The difference between cgetustr() 909 * and cgetstr() is that cgetustr does not decode escapes but rather treats 910 * all characters literally. A pointer to a NUL terminated malloc'd 911 * copy of the string is returned in the char pointed to by str. The 912 * length of the string not including the trailing NUL is returned on success, 913 * -1 if the requested string capability couldn't be found, -2 if a system 914 * error was encountered (storage allocation failure). 915 */ 916int 917cgetustr(buf, cap, str) 918 char *buf, *cap, **str; 919{ 920 u_int m_room; 921 char *bp, *mp; 922 int len; 923 char *mem; 924 925 /* 926 * Find string capability cap 927 */ 928 if ((bp = cgetcap(buf, cap, '=')) == NULL) 929 return (-1); 930 931 /* 932 * Conversion / storage allocation loop ... Allocate memory in 933 * chunks SFRAG in size. 934 */ 935 if ((mem = malloc(SFRAG)) == NULL) { 936 errno = ENOMEM; 937 return (-2); /* couldn't even allocate the first fragment */ 938 } 939 m_room = SFRAG; 940 mp = mem; 941 942 while (*bp != ':' && *bp != '\0') { 943 /* 944 * Loop invariants: 945 * There is always room for one more character in mem. 946 * Mp always points just past last character in mem. 947 * Bp always points at next character in buf. 948 */ 949 *mp++ = *bp++; 950 m_room--; 951 952 /* 953 * Enforce loop invariant: if no room left in current 954 * buffer, try to get some more. 955 */ 956 if (m_room == 0) { 957 size_t size = mp - mem; 958 959 if ((mem = reallocf(mem, size + SFRAG)) == NULL) 960 return (-2); 961 m_room = SFRAG; 962 mp = mem + size; 963 } 964 } 965 *mp++ = '\0'; /* loop invariant let's us do this */ 966 m_room--; 967 len = mp - mem - 1; 968 969 /* 970 * Give back any extra memory and return value and success. 971 */ 972 if (m_room != 0) 973 if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL) 974 return (-2); 975 *str = mem; 976 return (len); 977} 978 979/* 980 * Cgetnum retrieves the value of the numeric capability cap from the 981 * capability record pointed to by buf. The numeric value is returned in 982 * the long pointed to by num. 0 is returned on success, -1 if the requested 983 * numeric capability couldn't be found. 984 */ 985int 986cgetnum(buf, cap, num) 987 char *buf, *cap; 988 long *num; 989{ 990 long n; 991 int base, digit; 992 char *bp; 993 994 /* 995 * Find numeric capability cap 996 */ 997 bp = cgetcap(buf, cap, '#'); 998 if (bp == NULL) 999 return (-1); 1000 1001 /* 1002 * Look at value and determine numeric base: 1003 * 0x... or 0X... hexadecimal, 1004 * else 0... octal, 1005 * else decimal. 1006 */ 1007 if (*bp == '0') { 1008 bp++; 1009 if (*bp == 'x' || *bp == 'X') { 1010 bp++; 1011 base = 16; 1012 } else 1013 base = 8; 1014 } else 1015 base = 10; 1016 1017 /* 1018 * Conversion loop ... 1019 */ 1020 n = 0; 1021 for (;;) { 1022 if ('0' <= *bp && *bp <= '9') 1023 digit = *bp - '0'; 1024 else if ('a' <= *bp && *bp <= 'f') 1025 digit = 10 + *bp - 'a'; 1026 else if ('A' <= *bp && *bp <= 'F') 1027 digit = 10 + *bp - 'A'; 1028 else 1029 break; 1030 1031 if (digit >= base) 1032 break; 1033 1034 n = n * base + digit; 1035 bp++; 1036 } 1037 1038 /* 1039 * Return value and success. 1040 */ 1041 *num = n; 1042 return (0); 1043} 1044 1045 1046/* 1047 * Compare name field of record. 1048 */ 1049static int 1050nfcmp(nf, rec) 1051 char *nf, *rec; 1052{ 1053 char *cp, tmp; 1054 int ret; 1055 1056 for (cp = rec; *cp != ':'; cp++) 1057 ; 1058 1059 tmp = *(cp + 1); 1060 *(cp + 1) = '\0'; 1061 ret = strcmp(nf, rec); 1062 *(cp + 1) = tmp; 1063 1064 return (ret); 1065} 1066