1116742Ssam/* $NetBSD: getcap.c,v 1.29 1999/03/29 09:27:29 abs Exp $ */ 2116904Ssam 3186904Ssam/*- 4116742Ssam * Copyright (c) 1992, 1993 5116742Ssam * The Regents of the University of California. All rights reserved. 6116742Ssam * 7116742Ssam * This code is derived from software contributed to Berkeley by 8116742Ssam * Casey Leedom of Lawrence Livermore National Laboratory. 9116742Ssam * 10116742Ssam * Redistribution and use in source and binary forms, with or without 11116742Ssam * modification, are permitted provided that the following conditions 12116742Ssam * are met: 13116742Ssam * 1. Redistributions of source code must retain the above copyright 14116742Ssam * notice, this list of conditions and the following disclaimer. 15116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 16116904Ssam * notice, this list of conditions and the following disclaimer in the 17116904Ssam * documentation and/or other materials provided with the distribution. 18116904Ssam * 3. Neither the name of the University nor the names of its contributors 19116904Ssam * may be used to endorse or promote products derived from this software 20116904Ssam * without specific prior written permission. 21116904Ssam * 22116904Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23116904Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25116904Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26116742Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27116742Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28116742Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29116742Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30116742Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31138568Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32170530Ssam * SUCH DAMAGE. 33116742Ssam */ 34138568Ssam 35178354Ssam#include <config.h> 36178354Ssam 37178354Ssam#include "roken.h" 38178354Ssam 39178354Ssam#include <sys/types.h> 40178354Ssam#include <ctype.h> 41178354Ssam#if defined(HAVE_DB_185_H) 42178354Ssam#include <db_185.h> 43178354Ssam#elif defined(HAVE_DB_H) 44178354Ssam#include <db.h> 45138568Ssam#endif 46138568Ssam#include <errno.h> 47138568Ssam#include <fcntl.h> 48138568Ssam#include <limits.h> 49138568Ssam#include <stdio.h> 50138568Ssam#include <stdlib.h> 51138568Ssam#include <string.h> 52138568Ssam#include <unistd.h> 53138568Ssam 54170530Ssam#define BFRAG 1024 55138568Ssam#if 0 56172211Ssam#define BSIZE 1024 57172211Ssam#endif 58172211Ssam#define ESC ('[' & 037) /* ASCII ESC */ 59116742Ssam#define MAX_RECURSION 32 /* maximum getent recursion */ 60116742Ssam#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 61116742Ssam 62170530Ssam#define RECOK (char)0 63138568Ssam#define TCERR (char)1 64116742Ssam#define SHADOW (char)2 65138568Ssam 66138568Ssamstatic size_t topreclen; /* toprec length */ 67178354Ssamstatic char *toprec; /* Additional record specified by cgetset() */ 68138568Ssamstatic int gottoprec; /* Flag indicating retrieval of toprecord */ 69116742Ssam 70178354Ssam#if 0 /* 71178354Ssam * Don't use db support unless it's build into libc but we don't 72190093Srpaulo * check for that now, so just disable the code. 73178354Ssam */ 74178354Ssam#if defined(HAVE_DBOPEN) && defined(HAVE_DB_H) 75178354Ssam#define USE_DB 76178354Ssam#endif 77178354Ssam#endif 78178354Ssam 79178354Ssam#ifdef USE_DB 80178354Ssamstatic int cdbget (DB *, char **, const char *); 81178354Ssam#endif 82178354Ssamstatic int getent (char **, size_t *, char **, int, const char *, int, char *); 83186904Ssamstatic int nfcmp (char *, char *); 84193239Ssam 85178354Ssam 86178354SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetset(const char *ent); 87178354SsamROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL cgetcap(char *buf, const char *cap, int type); 88178354SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetent(char **buf, char **db_array, const char *name); 89178354SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetmatch(const char *buf, const char *name); 90178354SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetclose(void); 91116742Ssam#if 0 92116742Ssamint cgetfirst(char **buf, char **db_array); 93116742Ssamint cgetnext(char **bp, char **db_array); 94116742Ssam#endif 95116742SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetstr(char *buf, const char *cap, char **str); 96116742SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetustr(char *buf, const char *cap, char **str); 97178354SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetnum(char *buf, const char *cap, long *num); 98178354Ssam/* 99178354Ssam * Cgetset() allows the addition of a user specified buffer to be added 100178354Ssam * to the database array, in effect "pushing" the buffer on top of the 101178354Ssam * virtual database. 0 is returned on success, -1 on failure. 102178354Ssam */ 103120483SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 104183252Ssamcgetset(const char *ent) 105183252Ssam{ 106183252Ssam const char *source, *check; 107183252Ssam char *dest; 108183252Ssam 109183252Ssam if (ent == NULL) { 110183252Ssam if (toprec) 111183252Ssam free(toprec); 112183252Ssam toprec = NULL; 113183252Ssam topreclen = 0; 114183252Ssam return (0); 115183252Ssam } 116183252Ssam topreclen = strlen(ent); 117183255Ssam if ((toprec = malloc (topreclen + 1)) == NULL) { 118183255Ssam errno = ENOMEM; 119183256Ssam return (-1); 120183257Ssam } 121183257Ssam gottoprec = 0; 122186099Ssam 123183252Ssam source=ent; 124183252Ssam dest=toprec; 125183252Ssam while (*source) { /* Strip whitespace */ 126170530Ssam *dest++ = *source++; /* Do not check first field */ 127170530Ssam while (*source == ':') { 128170530Ssam check=source+1; 129170530Ssam while (*check && (isspace((unsigned char)*check) || 130170530Ssam (*check=='\\' && isspace((unsigned char)check[1])))) 131170530Ssam ++check; 132170530Ssam if( *check == ':' ) 133170530Ssam source=check; 134183252Ssam else 135170530Ssam break; 136183251Ssam 137173273Ssam } 138170530Ssam } 139178354Ssam *dest=0; 140172225Ssam 141191756Ssam return (0); 142172225Ssam} 143191756Ssam 144170530Ssam/* 145138568Ssam * Cgetcap searches the capability record buf for the capability cap with 146138568Ssam * type `type'. A pointer to the value of cap is returned on success, NULL 147116742Ssam * if the requested capability couldn't be found. 148116742Ssam * 149178354Ssam * Specifying a type of ':' means that nothing should follow cap (:cap:). 150170530Ssam * In this case a pointer to the terminating ':' or NUL will be returned if 151116742Ssam * cap is found. 152116742Ssam * 153170530Ssam * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 154170530Ssam * return NULL. 155116742Ssam */ 156116742SsamROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL 157138568Ssamcgetcap(char *buf, const char *cap, int type) 158170530Ssam{ 159178354Ssam char *bp; 160138568Ssam const char *cp; 161170530Ssam 162170530Ssam bp = buf; 163170530Ssam for (;;) { 164170530Ssam /* 165116742Ssam * Skip past the current capability field - it's either the 166170530Ssam * name field if this is the first time through the loop, or 167170530Ssam * the remainder of a field whose name failed to match cap. 168170530Ssam */ 169178354Ssam for (;;) 170170530Ssam if (*bp == '\0') 171170530Ssam return (NULL); 172170530Ssam else 173116742Ssam if (*bp++ == ':') 174170530Ssam break; 175170530Ssam 176170530Ssam /* 177170530Ssam * Try to match (cap, type) in buf. 178170530Ssam */ 179170530Ssam for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 180170530Ssam continue; 181170530Ssam if (*cp != '\0') 182170530Ssam continue; 183170530Ssam if (*bp == '@') 184170530Ssam return (NULL); 185170530Ssam if (type == ':') { 186116742Ssam if (*bp != '\0' && *bp != ':') 187138568Ssam continue; 188138568Ssam return(bp); 189178354Ssam } 190184288Ssam if (*bp != type) 191138568Ssam continue; 192178354Ssam bp++; 193178354Ssam return (*bp == '@' ? NULL : bp); 194178354Ssam } 195178354Ssam /* NOTREACHED */ 196193239Ssam} 197116742Ssam 198138568Ssam/* 199178354Ssam * Cgetent extracts the capability record name from the NULL terminated file 200116742Ssam * array db_array and returns a pointer to a malloc'd copy of it in buf. 201170530Ssam * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 202173273Ssam * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 203173273Ssam * -1 if the requested record couldn't be found, -2 if a system error was 204182828Ssam * encountered (couldn't open/read a file, etc.), and -3 if a potential 205182828Ssam * reference loop is detected. 206183255Ssam */ 207183257SsamROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 208183257Ssamcgetent(char **buf, char **db_array, const char *name) 209170530Ssam{ 210187797Ssam size_t dummy; 211187797Ssam 212187797Ssam return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 213187797Ssam} 214187797Ssam 215138568Ssam/* 216138568Ssam * Getent implements the functions of cgetent. If fd is non-negative, 217138568Ssam * *db_array has already been opened and fd is the open file descriptor. We 218138568Ssam * do this to save time and avoid using up file descriptors for tc= 219138568Ssam * recursions. 220138568Ssam * 221178354Ssam * Getent returns the same success/failure codes as cgetent. On success, a 222178354Ssam * pointer to a malloc'ed capability record with all tc= capabilities fully 223178354Ssam * expanded and its length (not including trailing ASCII NUL) are left in 224178354Ssam * *cap and *len. 225178354Ssam * 226178354Ssam * Basic algorithm: 227178354Ssam * + Allocate memory incrementally as needed in chunks of size BFRAG 228178354Ssam * for capability buffer. 229178354Ssam * + Recurse for each tc=name and interpolate result. Stop when all 230178354Ssam * names interpolated, a name can't be found, or depth exceeds 231178354Ssam * MAX_RECURSION. 232178354Ssam */ 233178354Ssamstatic int 234178354Ssamgetent(char **cap, size_t *len, char **db_array, int fd, 235178354Ssam const char *name, int depth, char *nfield) 236178354Ssam{ 237178354Ssam char *r_end, *rp = NULL, **db_p; /* pacify gcc */ 238178354Ssam int myfd = 0, eof, foundit; 239178354Ssam char *record; 240178354Ssam int tc_not_resolved; 241178354Ssam 242178354Ssam /* 243178354Ssam * Return with ``loop detected'' error if we've recursed more than 244178354Ssam * MAX_RECURSION times. 245178354Ssam */ 246178354Ssam if (depth > MAX_RECURSION) 247178354Ssam return (-3); 248178354Ssam 249178354Ssam /* 250178354Ssam * Check if we have a top record from cgetset(). 251178354Ssam */ 252178354Ssam if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 253116742Ssam size_t len = topreclen + BFRAG; 254116742Ssam if ((record = malloc (len)) == NULL) { 255116742Ssam errno = ENOMEM; 256138568Ssam return (-2); 257116742Ssam } 258116742Ssam (void)strlcpy(record, toprec, len); 259116742Ssam db_p = db_array; 260116742Ssam rp = record + topreclen + 1; 261116742Ssam r_end = rp + BFRAG; 262116742Ssam goto tc_exp; 263138568Ssam } 264116742Ssam /* 265116742Ssam * Allocate first chunk of memory. 266116742Ssam */ 267116742Ssam if ((record = malloc(BFRAG)) == NULL) { 268116742Ssam errno = ENOMEM; 269144618Ssam return (-2); 270144618Ssam } 271144618Ssam r_end = record + BFRAG; 272178354Ssam foundit = 0; 273178354Ssam /* 274178354Ssam * Loop through database array until finding the record. 275127877Ssam */ 276138568Ssam 277138568Ssam for (db_p = db_array; *db_p != NULL; db_p++) { 278138568Ssam eof = 0; 279138568Ssam 280138568Ssam /* 281116742Ssam * Open database if not already open. 282148302Ssam */ 283148302Ssam 284138568Ssam if (fd >= 0) { 285178354Ssam (void)lseek(fd, (off_t)0, SEEK_SET); 286178354Ssam } else { 287178354Ssam#ifdef USE_DB 288178354Ssam char pbuf[_POSIX_PATH_MAX]; 289178354Ssam char *cbuf; 290191746Sthompsa size_t clen; 291191746Sthompsa int retval; 292178354Ssam DB *capdbp; 293148306Ssam 294170530Ssam (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 295184274Ssam if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 296170530Ssam != NULL) { 297178354Ssam free(record); 298178354Ssam retval = cdbget(capdbp, &record, name); 299138568Ssam if (retval < 0) { 300178354Ssam /* no record available */ 301178354Ssam (void)capdbp->close(capdbp); 302178354Ssam return (retval); 303178354Ssam } 304178354Ssam /* save the data; close frees it */ 305178354Ssam clen = strlen(record); 306178354Ssam cbuf = malloc(clen + 1); 307138568Ssam if (cbuf == NULL) 308138568Ssam return (-2); 309178354Ssam memmove(cbuf, record, clen + 1); 310178354Ssam if (capdbp->close(capdbp) < 0) { 311178354Ssam free(cbuf); 312178354Ssam return (-2); 313138568Ssam } 314138568Ssam *len = clen; 315138568Ssam *cap = cbuf; 316138568Ssam return (retval); 317138568Ssam } else 318138568Ssam#endif 319170530Ssam { 320170530Ssam fd = open(*db_p, O_RDONLY, 0); 321178354Ssam if (fd < 0) { 322138568Ssam /* No error on unfound file. */ 323178354Ssam continue; 324138568Ssam } 325138568Ssam myfd = 1; 326138568Ssam } 327178354Ssam } 328178354Ssam /* 329178354Ssam * Find the requested capability record ... 330178354Ssam */ 331178354Ssam { 332178354Ssam char buf[BUFSIZ]; 333178354Ssam char *b_end, *bp, *cp; 334178354Ssam int c, slash; 335178354Ssam 336178354Ssam /* 337138568Ssam * Loop invariants: 338144618Ssam * There is always room for one more character in record. 339138568Ssam * R_end always points just past end of record. 340178354Ssam * Rp always points just past last character in record. 341178354Ssam * B_end always points just past last character in buf. 342178354Ssam * Bp always points at next character in buf. 343178354Ssam * Cp remembers where the last colon was. 344170530Ssam */ 345178354Ssam b_end = buf; 346138568Ssam bp = buf; 347178354Ssam cp = 0; 348178354Ssam slash = 0; 349178354Ssam for (;;) { 350178354Ssam 351178354Ssam /* 352178354Ssam * Read in a line implementing (\, newline) 353178354Ssam * line continuation. 354178354Ssam */ 355178354Ssam rp = record; 356178354Ssam for (;;) { 357170530Ssam if (bp >= b_end) { 358170530Ssam int n; 359138568Ssam 360148863Ssam n = read(fd, buf, sizeof(buf)); 361148863Ssam if (n <= 0) { 362170530Ssam if (myfd) 363148863Ssam (void)close(fd); 364178354Ssam if (n < 0) { 365170530Ssam free(record); 366170530Ssam return (-2); 367138568Ssam } else { 368138568Ssam fd = -1; 369178354Ssam eof = 1; 370178354Ssam break; 371138568Ssam } 372138568Ssam } 373178354Ssam b_end = buf+n; 374178354Ssam bp = buf; 375178354Ssam } 376178354Ssam 377178354Ssam c = *bp++; 378178354Ssam if (c == '\n') { 379178354Ssam if (slash) { 380178354Ssam slash = 0; 381178354Ssam rp--; 382178354Ssam continue; 383138568Ssam } else 384144618Ssam break; 385178354Ssam } 386178354Ssam if (slash) { 387170530Ssam slash = 0; 388178354Ssam cp = 0; 389178354Ssam } 390178354Ssam if (c == ':') { 391178354Ssam /* 392178354Ssam * If the field was `empty' (i.e. 393178354Ssam * contained only white space), back up 394178354Ssam * to the colon (eliminating the 395170530Ssam * field). 396170530Ssam */ 397148863Ssam if (cp) 398170530Ssam rp = cp; 399178354Ssam else 400178354Ssam cp = rp; 401138568Ssam } else if (c == '\\') { 402148863Ssam slash = 1; 403170530Ssam } else if (c != ' ' && c != '\t') { 404138568Ssam /* 405116742Ssam * Forget where the colon was, as this 406144618Ssam * is not an empty field. 407116742Ssam */ 408116742Ssam cp = 0; 409178354Ssam } 410144618Ssam *rp++ = c; 411138568Ssam 412144618Ssam /* 413138568Ssam * Enforce loop invariant: if no room 414178354Ssam * left in record buffer, try to get 415178354Ssam * some more. 416170530Ssam */ 417153073Ssam if (rp >= r_end) { 418153073Ssam u_int pos; 419153073Ssam size_t newsize; 420178354Ssam 421148936Ssam pos = rp - record; 422148936Ssam newsize = r_end - record + BFRAG; 423178354Ssam record = realloc(record, newsize); 424178354Ssam if (record == NULL) { 425178354Ssam errno = ENOMEM; 426178354Ssam if (myfd) 427116742Ssam (void)close(fd); 428 return (-2); 429 } 430 r_end = record + newsize; 431 rp = record + pos; 432 } 433 } 434 /* Eliminate any white space after the last colon. */ 435 if (cp) 436 rp = cp + 1; 437 /* Loop invariant lets us do this. */ 438 *rp++ = '\0'; 439 440 /* 441 * If encountered eof check next file. 442 */ 443 if (eof) 444 break; 445 446 /* 447 * Toss blank lines and comments. 448 */ 449 if (*record == '\0' || *record == '#') 450 continue; 451 452 /* 453 * See if this is the record we want ... 454 */ 455 if (cgetmatch(record, name) == 0) { 456 if (nfield == NULL || !nfcmp(nfield, record)) { 457 foundit = 1; 458 break; /* found it! */ 459 } 460 } 461 } 462 } 463 if (foundit) 464 break; 465 } 466 467 if (!foundit) 468 return (-1); 469 470 /* 471 * Got the capability record, but now we have to expand all tc=name 472 * references in it ... 473 */ 474 tc_exp: { 475 char *newicap, *s; 476 size_t ilen, newilen; 477 int diff, iret, tclen; 478 char *icap, *scan, *tc, *tcstart, *tcend; 479 480 /* 481 * Loop invariants: 482 * There is room for one more character in record. 483 * R_end points just past end of record. 484 * Rp points just past last character in record. 485 * Scan points at remainder of record that needs to be 486 * scanned for tc=name constructs. 487 */ 488 scan = record; 489 tc_not_resolved = 0; 490 for (;;) { 491 if ((tc = cgetcap(scan, "tc", '=')) == NULL) 492 break; 493 494 /* 495 * Find end of tc=name and stomp on the trailing `:' 496 * (if present) so we can use it to call ourselves. 497 */ 498 s = tc; 499 for (;;) 500 if (*s == '\0') 501 break; 502 else 503 if (*s++ == ':') { 504 *(s - 1) = '\0'; 505 break; 506 } 507 tcstart = tc - 3; 508 tclen = s - tcstart; 509 tcend = s; 510 511 iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 512 NULL); 513 newicap = icap; /* Put into a register. */ 514 newilen = ilen; 515 if (iret != 0) { 516 /* an error */ 517 if (iret < -1) { 518 if (myfd) 519 (void)close(fd); 520 free(record); 521 return (iret); 522 } 523 if (iret == 1) 524 tc_not_resolved = 1; 525 /* couldn't resolve tc */ 526 if (iret == -1) { 527 *(s - 1) = ':'; 528 scan = s - 1; 529 tc_not_resolved = 1; 530 continue; 531 532 } 533 } 534 /* not interested in name field of tc'ed record */ 535 s = newicap; 536 for (;;) 537 if (*s == '\0') 538 break; 539 else 540 if (*s++ == ':') 541 break; 542 newilen -= s - newicap; 543 newicap = s; 544 545 /* make sure interpolated record is `:'-terminated */ 546 s += newilen; 547 if (*(s-1) != ':') { 548 *s = ':'; /* overwrite NUL with : */ 549 newilen++; 550 } 551 552 /* 553 * Make sure there's enough room to insert the 554 * new record. 555 */ 556 diff = newilen - tclen; 557 if (diff >= r_end - rp) { 558 u_int pos, tcpos, tcposend; 559 size_t newsize; 560 561 pos = rp - record; 562 newsize = r_end - record + diff + BFRAG; 563 tcpos = tcstart - record; 564 tcposend = tcend - record; 565 record = realloc(record, newsize); 566 if (record == NULL) { 567 errno = ENOMEM; 568 if (myfd) 569 (void)close(fd); 570 free(icap); 571 return (-2); 572 } 573 r_end = record + newsize; 574 rp = record + pos; 575 tcstart = record + tcpos; 576 tcend = record + tcposend; 577 } 578 579 /* 580 * Insert tc'ed record into our record. 581 */ 582 s = tcstart + newilen; 583 memmove(s, tcend, (size_t)(rp - tcend)); 584 memmove(tcstart, newicap, newilen); 585 rp += diff; 586 free(icap); 587 588 /* 589 * Start scan on `:' so next cgetcap works properly 590 * (cgetcap always skips first field). 591 */ 592 scan = s-1; 593 } 594 595 } 596 /* 597 * Close file (if we opened it), give back any extra memory, and 598 * return capability, length and success. 599 */ 600 if (myfd) 601 (void)close(fd); 602 *len = rp - record - 1; /* don't count NUL */ 603 if (r_end > rp) 604 if ((record = 605 realloc(record, (size_t)(rp - record))) == NULL) { 606 errno = ENOMEM; 607 return (-2); 608 } 609 610 *cap = record; 611 if (tc_not_resolved) 612 return (1); 613 return (0); 614} 615 616#ifdef USE_DB 617static int 618cdbget(DB *capdbp, char **bp, const char *name) 619{ 620 DBT key; 621 DBT data; 622 623 /* LINTED key is not modified */ 624 key.data = (char *)name; 625 key.size = strlen(name); 626 627 for (;;) { 628 /* Get the reference. */ 629 switch(capdbp->get(capdbp, &key, &data, 0)) { 630 case -1: 631 return (-2); 632 case 1: 633 return (-1); 634 } 635 636 /* If not an index to another record, leave. */ 637 if (((char *)data.data)[0] != SHADOW) 638 break; 639 640 key.data = (char *)data.data + 1; 641 key.size = data.size - 1; 642 } 643 644 *bp = (char *)data.data + 1; 645 return (((char *)(data.data))[0] == TCERR ? 1 : 0); 646} 647#endif /* USE_DB */ 648 649/* 650 * Cgetmatch will return 0 if name is one of the names of the capability 651 * record buf, -1 if not. 652 */ 653int 654cgetmatch(const char *buf, const char *name) 655{ 656 const char *np, *bp; 657 658 /* 659 * Start search at beginning of record. 660 */ 661 bp = buf; 662 for (;;) { 663 /* 664 * Try to match a record name. 665 */ 666 np = name; 667 for (;;) 668 if (*np == '\0') { 669 if (*bp == '|' || *bp == ':' || *bp == '\0') 670 return (0); 671 else 672 break; 673 } else 674 if (*bp++ != *np++) 675 break; 676 677 /* 678 * Match failed, skip to next name in record. 679 */ 680 bp--; /* a '|' or ':' may have stopped the match */ 681 for (;;) 682 if (*bp == '\0' || *bp == ':') 683 return (-1); /* match failed totally */ 684 else 685 if (*bp++ == '|') 686 break; /* found next name */ 687 } 688} 689 690#if 0 691int 692cgetfirst(char **buf, char **db_array) 693{ 694 (void)cgetclose(); 695 return (cgetnext(buf, db_array)); 696} 697#endif 698 699static FILE *pfp; 700static int slash; 701static char **dbp; 702 703ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 704cgetclose(void) 705{ 706 if (pfp != NULL) { 707 (void)fclose(pfp); 708 pfp = NULL; 709 } 710 dbp = NULL; 711 gottoprec = 0; 712 slash = 0; 713 return(0); 714} 715 716#if 0 717/* 718 * Cgetnext() gets either the first or next entry in the logical database 719 * specified by db_array. It returns 0 upon completion of the database, 1 720 * upon returning an entry with more remaining, and -1 if an error occurs. 721 */ 722int 723cgetnext(char **bp, char **db_array) 724{ 725 size_t len; 726 int status, done; 727 char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 728 size_t dummy; 729 730 if (dbp == NULL) 731 dbp = db_array; 732 733 if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 734 (void)cgetclose(); 735 return (-1); 736 } 737 for(;;) { 738 if (toprec && !gottoprec) { 739 gottoprec = 1; 740 line = toprec; 741 } else { 742 line = fgetln(pfp, &len); 743 if (line == NULL && pfp) { 744 if (ferror(pfp)) { 745 (void)cgetclose(); 746 return (-1); 747 } else { 748 (void)fclose(pfp); 749 pfp = NULL; 750 if (*++dbp == NULL) { 751 (void)cgetclose(); 752 return (0); 753 } else if ((pfp = 754 fopen(*dbp, "r")) == NULL) { 755 (void)cgetclose(); 756 return (-1); 757 } else 758 continue; 759 } 760 } else 761 line[len - 1] = '\0'; 762 if (len == 1) { 763 slash = 0; 764 continue; 765 } 766 if (isspace((unsigned char)*line) || 767 *line == ':' || *line == '#' || slash) { 768 if (line[len - 2] == '\\') 769 slash = 1; 770 else 771 slash = 0; 772 continue; 773 } 774 if (line[len - 2] == '\\') 775 slash = 1; 776 else 777 slash = 0; 778 } 779 780 781 /* 782 * Line points to a name line. 783 */ 784 done = 0; 785 np = nbuf; 786 for (;;) { 787 for (cp = line; *cp != '\0'; cp++) { 788 if (*cp == ':') { 789 *np++ = ':'; 790 done = 1; 791 break; 792 } 793 if (*cp == '\\') 794 break; 795 *np++ = *cp; 796 } 797 if (done) { 798 *np = '\0'; 799 break; 800 } else { /* name field extends beyond the line */ 801 line = fgetln(pfp, &len); 802 if (line == NULL && pfp) { 803 if (ferror(pfp)) { 804 (void)cgetclose(); 805 return (-1); 806 } 807 (void)fclose(pfp); 808 pfp = NULL; 809 *np = '\0'; 810 break; 811 } else 812 line[len - 1] = '\0'; 813 } 814 } 815 rp = buf; 816 for(cp = nbuf; *cp != '\0'; cp++) 817 if (*cp == '|' || *cp == ':') 818 break; 819 else 820 *rp++ = *cp; 821 822 *rp = '\0'; 823 /* 824 * XXX 825 * Last argument of getent here should be nbuf if we want true 826 * sequential access in the case of duplicates. 827 * With NULL, getent will return the first entry found 828 * rather than the duplicate entry record. This is a 829 * matter of semantics that should be resolved. 830 */ 831 status = getent(bp, &dummy, db_array, -1, buf, 0, NULL); 832 if (status == -2 || status == -3) 833 (void)cgetclose(); 834 835 return (status + 1); 836 } 837 /* NOTREACHED */ 838} 839#endif 840 841/* 842 * Cgetstr retrieves the value of the string capability cap from the 843 * capability record pointed to by buf. A pointer to a decoded, NUL 844 * terminated, malloc'd copy of the string is returned in the char * 845 * pointed to by str. The length of the string not including the trailing 846 * NUL is returned on success, -1 if the requested string capability 847 * couldn't be found, -2 if a system error was encountered (storage 848 * allocation failure). 849 */ 850ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 851cgetstr(char *buf, const char *cap, char **str) 852{ 853 u_int m_room; 854 const char *bp; 855 char *mp; 856 int len; 857 char *mem, *nmem; 858 859 *str = NULL; 860 861 /* 862 * Find string capability cap 863 */ 864 bp = cgetcap(buf, cap, '='); 865 if (bp == NULL) 866 return (-1); 867 868 /* 869 * Conversion / storage allocation loop ... Allocate memory in 870 * chunks SFRAG in size. 871 */ 872 if ((mem = malloc(SFRAG)) == NULL) { 873 errno = ENOMEM; 874 return (-2); /* couldn't even allocate the first fragment */ 875 } 876 m_room = SFRAG; 877 mp = mem; 878 879 while (*bp != ':' && *bp != '\0') { 880 /* 881 * Loop invariants: 882 * There is always room for one more character in mem. 883 * Mp always points just past last character in mem. 884 * Bp always points at next character in buf. 885 */ 886 if (*bp == '^') { 887 bp++; 888 if (*bp == ':' || *bp == '\0') 889 break; /* drop unfinished escape */ 890 *mp++ = *bp++ & 037; 891 } else if (*bp == '\\') { 892 bp++; 893 if (*bp == ':' || *bp == '\0') 894 break; /* drop unfinished escape */ 895 if ('0' <= *bp && *bp <= '7') { 896 int n, i; 897 898 n = 0; 899 i = 3; /* maximum of three octal digits */ 900 do { 901 n = n * 8 + (*bp++ - '0'); 902 } while (--i && '0' <= *bp && *bp <= '7'); 903 *mp++ = n; 904 } 905 else switch (*bp++) { 906 case 'b': case 'B': 907 *mp++ = '\b'; 908 break; 909 case 't': case 'T': 910 *mp++ = '\t'; 911 break; 912 case 'n': case 'N': 913 *mp++ = '\n'; 914 break; 915 case 'f': case 'F': 916 *mp++ = '\f'; 917 break; 918 case 'r': case 'R': 919 *mp++ = '\r'; 920 break; 921 case 'e': case 'E': 922 *mp++ = ESC; 923 break; 924 case 'c': case 'C': 925 *mp++ = ':'; 926 break; 927 default: 928 /* 929 * Catches '\', '^', and 930 * everything else. 931 */ 932 *mp++ = *(bp-1); 933 break; 934 } 935 } else 936 *mp++ = *bp++; 937 m_room--; 938 939 /* 940 * Enforce loop invariant: if no room left in current 941 * buffer, try to get some more. 942 */ 943 if (m_room == 0) { 944 size_t size = mp - mem; 945 946 if ((nmem = realloc(mem, size + SFRAG)) == NULL) { 947 free(mem); 948 return (-2); 949 } 950 mem = nmem; 951 m_room = SFRAG; 952 mp = mem + size; 953 } 954 } 955 *mp++ = '\0'; /* loop invariant let's us do this */ 956 m_room--; 957 len = mp - mem - 1; 958 959 /* 960 * Give back any extra memory and return value and success. 961 */ 962 if (m_room != 0) { 963 if ((nmem = realloc(mem, (size_t)(mp - mem))) == NULL) { 964 free(mem); 965 return (-2); 966 } 967 mem = nmem; 968 } 969 *str = mem; 970 return (len); 971} 972 973/* 974 * Cgetustr retrieves the value of the string capability cap from the 975 * capability record pointed to by buf. The difference between cgetustr() 976 * and cgetstr() is that cgetustr does not decode escapes but rather treats 977 * all characters literally. A pointer to a NUL terminated malloc'd 978 * copy of the string is returned in the char pointed to by str. The 979 * length of the string not including the trailing NUL is returned on success, 980 * -1 if the requested string capability couldn't be found, -2 if a system 981 * error was encountered (storage allocation failure). 982 */ 983ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 984cgetustr(char *buf, const char *cap, char **str) 985{ 986 u_int m_room; 987 const char *bp; 988 char *mp; 989 int len; 990 char *mem; 991 992 /* 993 * Find string capability cap 994 */ 995 if ((bp = cgetcap(buf, cap, '=')) == NULL) 996 return (-1); 997 998 /* 999 * Conversion / storage allocation loop ... Allocate memory in 1000 * chunks SFRAG in size. 1001 */ 1002 if ((mem = malloc(SFRAG)) == NULL) { 1003 errno = ENOMEM; 1004 return (-2); /* couldn't even allocate the first fragment */ 1005 } 1006 m_room = SFRAG; 1007 mp = mem; 1008 1009 while (*bp != ':' && *bp != '\0') { 1010 /* 1011 * Loop invariants: 1012 * There is always room for one more character in mem. 1013 * Mp always points just past last character in mem. 1014 * Bp always points at next character in buf. 1015 */ 1016 *mp++ = *bp++; 1017 m_room--; 1018 1019 /* 1020 * Enforce loop invariant: if no room left in current 1021 * buffer, try to get some more. 1022 */ 1023 if (m_room == 0) { 1024 size_t size = mp - mem; 1025 1026 if ((mem = realloc(mem, size + SFRAG)) == NULL) 1027 return (-2); 1028 m_room = SFRAG; 1029 mp = mem + size; 1030 } 1031 } 1032 *mp++ = '\0'; /* loop invariant let's us do this */ 1033 m_room--; 1034 len = mp - mem - 1; 1035 1036 /* 1037 * Give back any extra memory and return value and success. 1038 */ 1039 if (m_room != 0) 1040 if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL) 1041 return (-2); 1042 *str = mem; 1043 return (len); 1044} 1045 1046/* 1047 * Cgetnum retrieves the value of the numeric capability cap from the 1048 * capability record pointed to by buf. The numeric value is returned in 1049 * the long pointed to by num. 0 is returned on success, -1 if the requested 1050 * numeric capability couldn't be found. 1051 */ 1052ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL 1053cgetnum(char *buf, const char *cap, long *num) 1054{ 1055 long n; 1056 int base, digit; 1057 const char *bp; 1058 1059 /* 1060 * Find numeric capability cap 1061 */ 1062 bp = cgetcap(buf, cap, '#'); 1063 if (bp == NULL) 1064 return (-1); 1065 1066 /* 1067 * Look at value and determine numeric base: 1068 * 0x... or 0X... hexadecimal, 1069 * else 0... octal, 1070 * else decimal. 1071 */ 1072 if (*bp == '0') { 1073 bp++; 1074 if (*bp == 'x' || *bp == 'X') { 1075 bp++; 1076 base = 16; 1077 } else 1078 base = 8; 1079 } else 1080 base = 10; 1081 1082 /* 1083 * Conversion loop ... 1084 */ 1085 n = 0; 1086 for (;;) { 1087 if ('0' <= *bp && *bp <= '9') 1088 digit = *bp - '0'; 1089 else if ('a' <= *bp && *bp <= 'f') 1090 digit = 10 + *bp - 'a'; 1091 else if ('A' <= *bp && *bp <= 'F') 1092 digit = 10 + *bp - 'A'; 1093 else 1094 break; 1095 1096 if (digit >= base) 1097 break; 1098 1099 n = n * base + digit; 1100 bp++; 1101 } 1102 1103 /* 1104 * Return value and success. 1105 */ 1106 *num = n; 1107 return (0); 1108} 1109 1110 1111/* 1112 * Compare name field of record. 1113 */ 1114static int 1115nfcmp(char *nf, char *rec) 1116{ 1117 char *cp, tmp; 1118 int ret; 1119 1120 for (cp = rec; *cp != ':'; cp++) 1121 ; 1122 1123 tmp = *(cp + 1); 1124 *(cp + 1) = '\0'; 1125 ret = strcmp(nf, rec); 1126 *(cp + 1) = tmp; 1127 1128 return (ret); 1129} 1130