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