Annotation of src/usr.bin/cap_mkdb/getinfo.c, Revision 1.2
1.2 ! naddy 1: /* $OpenBSD: getinfo.c,v 1.1 1999/03/05 04:47:45 tholo 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.
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
1.2 ! naddy 36: static char rcsid[] = "$OpenBSD: getinfo.c,v 1.1 1999/03/05 04:47:45 tholo Exp $";
1.1 tholo 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: * Toss blank lines and comments.
294: */
295: if (*record == '\0' || *record == '#')
296: continue;
297:
298: /*
299: * See if this is the record we want ...
300: */
301: if (igetmatch(record, name) == 0) {
302: foundit = 1;
303: break; /* found it! */
304: }
1.2 ! naddy 305:
! 306: /*
! 307: * If encountered eof check next file.
! 308: */
! 309: if (eof)
! 310: break;
1.1 tholo 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: }