Annotation of src/usr.bin/cap_mkdb/getinfo.c, Revision 1.1
1.1 ! tholo 1: /* $OpenBSD: getinfo.c,v 1.4 1998/07/13 02:11:32 millert Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1992, 1993
! 5: * The Regents of the University of California.
! 6: * Copyright (c) 1996 SigmaSoft, Th. Lockert <tholo@sigmasoft.com>
! 7: * All rights reserved.
! 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. All advertising materials mentioning features or use of this software
! 18: * must display the following acknowledgement:
! 19: * This product includes software developed by SigmaSoft, Th. Lockert.
! 20: * 4. The name of the author may not be used to endorse or promote products
! 21: * derived from this software without specific prior written permission.
! 22: *
! 23: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
! 24: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
! 25: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
! 26: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
! 27: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
! 28: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
! 29: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
! 30: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
! 31: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
! 32: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
! 33: */
! 34:
! 35: #ifndef lint
! 36: static char rcsid[] = "$OpenBSD: getinfo.c,v 1.4 1998/07/13 02:11:32 millert Exp $";
! 37: #endif /* not lint */
! 38:
! 39: #include <sys/types.h>
! 40:
! 41: #include <ctype.h>
! 42: #include <db.h>
! 43: #include <errno.h>
! 44: #include <fcntl.h>
! 45: #include <limits.h>
! 46: #include <stdio.h>
! 47: #include <stdlib.h>
! 48: #include <string.h>
! 49: #include <unistd.h>
! 50:
! 51: #define BFRAG 1024
! 52: #define BSIZE 1024
! 53: #define ESC ('[' & 037) /* ASCII ESC */
! 54: #define MAX_RECURSION 32 /* maximum getent recursion */
! 55: #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
! 56:
! 57: #define RECOK (char)0
! 58: #define TCERR (char)1
! 59: #define SHADOW (char)2
! 60:
! 61: static int getent __P((char **, u_int *, char **, int, char *, int));
! 62: static char *igetcap __P((char *, char *, int));
! 63: static int igetmatch __P((char *, char *));
! 64: static int igetclose __P((void));
! 65:
! 66: int igetnext __P((char **, char **));
! 67:
! 68: /*
! 69: * Cgetcap searches the capability record buf for the capability cap with
! 70: * type `type'. A pointer to the value of cap is returned on success, NULL
! 71: * if the requested capability couldn't be found.
! 72: *
! 73: * Specifying a type of ',' means that nothing should follow cap (,cap,).
! 74: * In this case a pointer to the terminating ',' or NUL will be returned if
! 75: * cap is found.
! 76: *
! 77: * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
! 78: * return NULL.
! 79: */
! 80: static char *
! 81: igetcap(buf, cap, type)
! 82: char *buf, *cap;
! 83: int type;
! 84: {
! 85: register char *bp, *cp;
! 86:
! 87: bp = buf;
! 88: for (;;) {
! 89: /*
! 90: * Skip past the current capability field - it's either the
! 91: * name field if this is the first time through the loop, or
! 92: * the remainder of a field whose name failed to match cap.
! 93: */
! 94: for (;;)
! 95: if (*bp == '\0')
! 96: return (NULL);
! 97: else
! 98: if (*bp++ == ',')
! 99: break;
! 100:
! 101: /*
! 102: * Try to match (cap, type) in buf.
! 103: */
! 104: for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
! 105: continue;
! 106: if (*cp != '\0')
! 107: continue;
! 108: if (*bp == '@')
! 109: return (NULL);
! 110: if (type == ',') {
! 111: if (*bp != '\0' && *bp != ',')
! 112: continue;
! 113: return(bp);
! 114: }
! 115: if (*bp != type)
! 116: continue;
! 117: bp++;
! 118: return (*bp == '@' ? NULL : bp);
! 119: }
! 120: /* NOTREACHED */
! 121: }
! 122:
! 123: /*
! 124: * Getent implements the functions of igetent. If fd is non-negative,
! 125: * *db_array has already been opened and fd is the open file descriptor. We
! 126: * do this to save time and avoid using up file descriptors for use=
! 127: * recursions.
! 128: *
! 129: * Getent returns the same success/failure codes as igetent. On success, a
! 130: * pointer to a malloc'ed capability record with all use= capabilities fully
! 131: * expanded and its length (not including trailing ASCII NUL) are left in
! 132: * *cap and *len.
! 133: *
! 134: * Basic algorithm:
! 135: * + Allocate memory incrementally as needed in chunks of size BFRAG
! 136: * for capability buffer.
! 137: * + Recurse for each use=name and interpolate result. Stop when all
! 138: * names interpolated, a name can't be found, or depth exceeds
! 139: * MAX_RECURSION.
! 140: */
! 141: static int
! 142: getent(cap, len, db_array, fd, name, depth)
! 143: char **cap, **db_array, *name;
! 144: u_int *len;
! 145: int fd, depth;
! 146: {
! 147: register char *r_end, *rp, **db_p;
! 148: int myfd, eof, foundit;
! 149: char *record;
! 150: int tc_not_resolved;
! 151:
! 152: /*
! 153: * Return with ``loop detected'' error if we've recursed more than
! 154: * MAX_RECURSION times.
! 155: */
! 156: if (depth > MAX_RECURSION)
! 157: return (-3);
! 158:
! 159: /*
! 160: * Allocate first chunk of memory.
! 161: */
! 162: if ((record = malloc(BFRAG)) == NULL) {
! 163: errno = ENOMEM;
! 164: return (-2);
! 165: }
! 166: r_end = record + BFRAG;
! 167: foundit = 0;
! 168: rp = NULL;
! 169: myfd = -1;
! 170: /*
! 171: * Loop through database array until finding the record.
! 172: */
! 173:
! 174: for (db_p = db_array; *db_p != NULL; db_p++) {
! 175: eof = 0;
! 176:
! 177: /*
! 178: * Open database if not already open.
! 179: */
! 180:
! 181: if (fd >= 0) {
! 182: (void)lseek(fd, (off_t)0, SEEK_SET);
! 183: myfd = 0;
! 184: } else {
! 185: fd = open(*db_p, O_RDONLY, 0);
! 186: if (fd < 0) {
! 187: /* No error on unfound file. */
! 188: continue;
! 189: }
! 190: myfd = 1;
! 191: }
! 192: /*
! 193: * Find the requested capability record ...
! 194: */
! 195: {
! 196: char buf[BUFSIZ];
! 197: register char *b_end, *bp;
! 198: register int c;
! 199:
! 200: /*
! 201: * Loop invariants:
! 202: * There is always room for one more character in record.
! 203: * R_end always points just past end of record.
! 204: * Rp always points just past last character in record.
! 205: * B_end always points just past last character in buf.
! 206: * Bp always points at next character in buf.
! 207: */
! 208: b_end = buf;
! 209: bp = buf;
! 210: for (;;) {
! 211:
! 212: /*
! 213: * Read in a line implementing (\, newline)
! 214: * line continuation.
! 215: */
! 216: rp = record;
! 217: for (;;) {
! 218: if (bp >= b_end) {
! 219: int n;
! 220:
! 221: n = read(fd, buf, sizeof(buf));
! 222: if (n <= 0) {
! 223: if (myfd)
! 224: (void)close(fd);
! 225: if (n < 0) {
! 226: free(record);
! 227: return (-2);
! 228: } else {
! 229: fd = -1;
! 230: eof = 1;
! 231: break;
! 232: }
! 233: }
! 234: b_end = buf+n;
! 235: bp = buf;
! 236: }
! 237:
! 238: c = *bp++;
! 239: if (c == '\n') {
! 240: if (bp >= b_end) {
! 241: int n;
! 242:
! 243: n = read(fd, buf, sizeof(buf));
! 244: if (n <= 0) {
! 245: if (myfd)
! 246: (void)close(fd);
! 247: if (n < 0) {
! 248: free(record);
! 249: return (-2);
! 250: } else {
! 251: fd = -1;
! 252: eof = 1;
! 253: break;
! 254: }
! 255: }
! 256: b_end = buf+n;
! 257: bp = buf;
! 258: }
! 259: if (rp > record && isspace(*bp))
! 260: continue;
! 261: else
! 262: break;
! 263: }
! 264: if (rp <= record || *(rp - 1) != ',' || !isspace(c))
! 265: *rp++ = c;
! 266:
! 267: /*
! 268: * Enforce loop invariant: if no room
! 269: * left in record buffer, try to get
! 270: * some more.
! 271: */
! 272: if (rp >= r_end) {
! 273: u_int pos;
! 274: size_t newsize;
! 275:
! 276: pos = rp - record;
! 277: newsize = r_end - record + BFRAG;
! 278: record = realloc(record, newsize);
! 279: if (record == NULL) {
! 280: errno = ENOMEM;
! 281: if (myfd)
! 282: (void)close(fd);
! 283: return (-2);
! 284: }
! 285: r_end = record + newsize;
! 286: rp = record + pos;
! 287: }
! 288: }
! 289: /* loop invariant let's us do this */
! 290: *rp++ = '\0';
! 291:
! 292: /*
! 293: * If encountered eof check next file.
! 294: */
! 295: if (eof)
! 296: break;
! 297:
! 298: /*
! 299: * Toss blank lines and comments.
! 300: */
! 301: if (*record == '\0' || *record == '#')
! 302: continue;
! 303:
! 304: /*
! 305: * See if this is the record we want ...
! 306: */
! 307: if (igetmatch(record, name) == 0) {
! 308: foundit = 1;
! 309: break; /* found it! */
! 310: }
! 311: }
! 312: }
! 313: if (foundit)
! 314: break;
! 315: }
! 316:
! 317: if (!foundit)
! 318: return (-1);
! 319:
! 320: /*
! 321: * Got the capability record, but now we have to expand all use=name
! 322: * references in it ...
! 323: */
! 324: {
! 325: register char *newicap, *s;
! 326: register int newilen;
! 327: u_int ilen;
! 328: int diff, iret, tclen;
! 329: char *icap, *scan, *tc, *tcstart, *tcend;
! 330:
! 331: /*
! 332: * Loop invariants:
! 333: * There is room for one more character in record.
! 334: * R_end points just past end of record.
! 335: * Rp points just past last character in record.
! 336: * Scan points at remainder of record that needs to be
! 337: * scanned for use=name constructs.
! 338: */
! 339: scan = record;
! 340: tc_not_resolved = 0;
! 341: for (;;) {
! 342: if ((tc = igetcap(scan, "use", '=')) == NULL)
! 343: break;
! 344:
! 345: /*
! 346: * Find end of use=name and stomp on the trailing `,'
! 347: * (if present) so we can use it to call ourselves.
! 348: */
! 349: s = tc;
! 350: for (;;)
! 351: if (*s == '\0')
! 352: break;
! 353: else
! 354: if (*s++ == ',') {
! 355: *(s - 1) = '\0';
! 356: break;
! 357: }
! 358: tcstart = tc - 4;
! 359: tclen = s - tcstart;
! 360: tcend = s;
! 361:
! 362: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1);
! 363: newicap = icap; /* Put into a register. */
! 364: newilen = ilen;
! 365: if (iret != 0) {
! 366: /* an error */
! 367: if (iret < -1) {
! 368: if (myfd)
! 369: (void)close(fd);
! 370: free(record);
! 371: return (iret);
! 372: }
! 373: if (iret == 1)
! 374: tc_not_resolved = 1;
! 375: /* couldn't resolve tc */
! 376: if (iret == -1) {
! 377: *(s - 1) = ',';
! 378: scan = s - 1;
! 379: tc_not_resolved = 1;
! 380: continue;
! 381:
! 382: }
! 383: }
! 384: /* not interested in name field of tc'ed record */
! 385: s = newicap;
! 386: for (;;)
! 387: if (*s == '\0')
! 388: break;
! 389: else
! 390: if (*s++ == ',')
! 391: break;
! 392: newilen -= s - newicap;
! 393: newicap = s;
! 394:
! 395: /* make sure interpolated record is `,'-terminated */
! 396: s += newilen;
! 397: if (*(s-1) != ',') {
! 398: *s = ','; /* overwrite NUL with , */
! 399: newilen++;
! 400: }
! 401:
! 402: /*
! 403: * Make sure there's enough room to insert the
! 404: * new record.
! 405: */
! 406: diff = newilen - tclen;
! 407: if (diff >= r_end - rp) {
! 408: u_int pos, tcpos, tcposend;
! 409: size_t newsize;
! 410:
! 411: pos = rp - record;
! 412: newsize = r_end - record + diff + BFRAG;
! 413: tcpos = tcstart - record;
! 414: tcposend = tcend - record;
! 415: record = realloc(record, newsize);
! 416: if (record == NULL) {
! 417: errno = ENOMEM;
! 418: if (myfd)
! 419: (void)close(fd);
! 420: free(icap);
! 421: return (-2);
! 422: }
! 423: r_end = record + newsize;
! 424: rp = record + pos;
! 425: tcstart = record + tcpos;
! 426: tcend = record + tcposend;
! 427: }
! 428:
! 429: /*
! 430: * Insert tc'ed record into our record.
! 431: */
! 432: s = tcstart + newilen;
! 433: bcopy(tcend, s, (size_t)(rp - tcend));
! 434: bcopy(newicap, tcstart, (size_t)newilen);
! 435: rp += diff;
! 436: free(icap);
! 437:
! 438: /*
! 439: * Start scan on `,' so next igetcap works properly
! 440: * (igetcap always skips first field).
! 441: */
! 442: scan = s-1;
! 443: }
! 444:
! 445: }
! 446: /*
! 447: * Close file (if we opened it), give back any extra memory, and
! 448: * return capability, length and success.
! 449: */
! 450: if (myfd)
! 451: (void)close(fd);
! 452: *len = rp - record - 1; /* don't count NUL */
! 453: if (r_end > rp)
! 454: if ((record =
! 455: realloc(record, (size_t)(rp - record))) == NULL) {
! 456: errno = ENOMEM;
! 457: return (-2);
! 458: }
! 459:
! 460: *cap = record;
! 461: if (tc_not_resolved)
! 462: return (1);
! 463: return (0);
! 464: }
! 465:
! 466: /*
! 467: * Cgetmatch will return 0 if name is one of the names of the capability
! 468: * record buf, -1 if not.
! 469: */
! 470: static int
! 471: igetmatch(buf, name)
! 472: char *buf, *name;
! 473: {
! 474: register char *np, *bp;
! 475:
! 476: /*
! 477: * Start search at beginning of record.
! 478: */
! 479: bp = buf;
! 480: for (;;) {
! 481: /*
! 482: * Try to match a record name.
! 483: */
! 484: np = name;
! 485: for (;;)
! 486: if (*np == '\0') {
! 487: if (*bp == '|' || *bp == ',' || *bp == '\0')
! 488: return (0);
! 489: else
! 490: break;
! 491: } else {
! 492: if (*bp++ != *np++)
! 493: break;
! 494: }
! 495:
! 496: /*
! 497: * Match failed, skip to next name in record.
! 498: */
! 499: bp--; /* a '|' or ',' may have stopped the match */
! 500: for (;;)
! 501: if (*bp == '\0' || *bp == ',')
! 502: return (-1); /* match failed totally */
! 503: else
! 504: if (*bp++ == '|')
! 505: break; /* found next name */
! 506: }
! 507: }
! 508:
! 509: static FILE *pfp;
! 510: static int slash;
! 511: static char **dbp;
! 512:
! 513: static int
! 514: igetclose()
! 515: {
! 516: if (pfp != NULL) {
! 517: (void)fclose(pfp);
! 518: pfp = NULL;
! 519: }
! 520: dbp = NULL;
! 521: slash = 0;
! 522: return(0);
! 523: }
! 524:
! 525: /*
! 526: * Cgetnext() gets either the first or next entry in the logical database
! 527: * specified by db_array. It returns 0 upon completion of the database, 1
! 528: * upon returning an entry with more remaining, and -1 if an error occurs.
! 529: */
! 530: int
! 531: igetnext(bp, db_array)
! 532: register char **bp;
! 533: char **db_array;
! 534: {
! 535: size_t len;
! 536: int status, done;
! 537: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
! 538: u_int dummy;
! 539:
! 540: if (dbp == NULL)
! 541: dbp = db_array;
! 542:
! 543: if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
! 544: (void)igetclose();
! 545: return (-1);
! 546: }
! 547: for(;;) {
! 548: line = fgetln(pfp, &len);
! 549: if (line == NULL && pfp) {
! 550: (void)fclose(pfp);
! 551: if (ferror(pfp)) {
! 552: (void)igetclose();
! 553: return (-1);
! 554: } else {
! 555: if (*++dbp == NULL) {
! 556: (void)igetclose();
! 557: return (0);
! 558: } else if ((pfp =
! 559: fopen(*dbp, "r")) == NULL) {
! 560: (void)igetclose();
! 561: return (-1);
! 562: } else
! 563: continue;
! 564: }
! 565: } else
! 566: line[len - 1] = '\0';
! 567: if (len == 1) {
! 568: slash = 0;
! 569: continue;
! 570: }
! 571: if (isspace(*line) ||
! 572: *line == ',' || *line == '#' || slash) {
! 573: if (line[len - 2] == '\\')
! 574: slash = 1;
! 575: else
! 576: slash = 0;
! 577: continue;
! 578: }
! 579: if (line[len - 2] == '\\')
! 580: slash = 1;
! 581: else
! 582: slash = 0;
! 583:
! 584: /*
! 585: * Line points to a name line.
! 586: */
! 587: done = 0;
! 588: np = nbuf;
! 589: for (;;) {
! 590: for (cp = line; *cp != '\0'; cp++) {
! 591: if (*cp == ',') {
! 592: *np++ = ',';
! 593: done = 1;
! 594: break;
! 595: }
! 596: *np++ = *cp;
! 597: }
! 598: if (done) {
! 599: *np = '\0';
! 600: break;
! 601: } else { /* name field extends beyond the line */
! 602: line = fgetln(pfp, &len);
! 603: if (line == NULL && pfp) {
! 604: (void)fclose(pfp);
! 605: if (ferror(pfp)) {
! 606: (void)igetclose();
! 607: return (-1);
! 608: }
! 609: } else
! 610: line[len - 1] = '\0';
! 611: }
! 612: }
! 613: rp = buf;
! 614: for(cp = nbuf; *cp != NULL; cp++)
! 615: if (*cp == '|' || *cp == ',')
! 616: break;
! 617: else
! 618: *rp++ = *cp;
! 619:
! 620: *rp = '\0';
! 621: status = getent(bp, &dummy, db_array, -1, buf, 0);
! 622: if (status == -2 || status == -3)
! 623: (void)igetclose();
! 624:
! 625: return (status + 1);
! 626: }
! 627: /* NOTREACHED */
! 628: }