Annotation of src/usr.bin/cap_mkdb/getinfo.c, Revision 1.4
1.4 ! mpech 1: /* $OpenBSD: getinfo.c,v 1.3 2001/06/18 18:17:39 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.
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.4 ! mpech 36: static char rcsid[] = "$OpenBSD: getinfo.c,v 1.3 2001/06/18 18:17:39 millert Exp $";
1.1 tholo 37: #endif /* not lint */
38:
39: #include <sys/types.h>
40:
41: #include <ctype.h>
42: #include <errno.h>
43: #include <fcntl.h>
44: #include <limits.h>
45: #include <stdio.h>
46: #include <stdlib.h>
47: #include <string.h>
48: #include <unistd.h>
49:
50: #define BFRAG 1024
51: #define BSIZE 1024
52: #define ESC ('[' & 037) /* ASCII ESC */
53: #define MAX_RECURSION 32 /* maximum getent recursion */
54: #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
55:
56: #define RECOK (char)0
57: #define TCERR (char)1
58: #define SHADOW (char)2
59:
60: static int getent __P((char **, u_int *, char **, int, char *, int));
61: static char *igetcap __P((char *, char *, int));
62: static int igetmatch __P((char *, char *));
63: static int igetclose __P((void));
64:
65: int igetnext __P((char **, char **));
66:
67: /*
68: * Cgetcap searches the capability record buf for the capability cap with
69: * type `type'. A pointer to the value of cap is returned on success, NULL
70: * if the requested capability couldn't be found.
71: *
72: * Specifying a type of ',' means that nothing should follow cap (,cap,).
73: * In this case a pointer to the terminating ',' or NUL will be returned if
74: * cap is found.
75: *
76: * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
77: * return NULL.
78: */
79: static char *
80: igetcap(buf, cap, type)
81: char *buf, *cap;
82: int type;
83: {
1.4 ! mpech 84: char *bp, *cp;
1.1 tholo 85:
86: bp = buf;
87: for (;;) {
88: /*
89: * Skip past the current capability field - it's either the
90: * name field if this is the first time through the loop, or
91: * the remainder of a field whose name failed to match cap.
92: */
93: for (;;)
94: if (*bp == '\0')
95: return (NULL);
96: else
97: if (*bp++ == ',')
98: break;
99:
100: /*
101: * Try to match (cap, type) in buf.
102: */
103: for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
104: continue;
105: if (*cp != '\0')
106: continue;
107: if (*bp == '@')
108: return (NULL);
109: if (type == ',') {
110: if (*bp != '\0' && *bp != ',')
111: continue;
112: return(bp);
113: }
114: if (*bp != type)
115: continue;
116: bp++;
117: return (*bp == '@' ? NULL : bp);
118: }
119: /* NOTREACHED */
120: }
121:
122: /*
123: * Getent implements the functions of igetent. If fd is non-negative,
124: * *db_array has already been opened and fd is the open file descriptor. We
125: * do this to save time and avoid using up file descriptors for use=
126: * recursions.
127: *
128: * Getent returns the same success/failure codes as igetent. On success, a
129: * pointer to a malloc'ed capability record with all use= capabilities fully
130: * expanded and its length (not including trailing ASCII NUL) are left in
131: * *cap and *len.
132: *
133: * Basic algorithm:
134: * + Allocate memory incrementally as needed in chunks of size BFRAG
135: * for capability buffer.
136: * + Recurse for each use=name and interpolate result. Stop when all
137: * names interpolated, a name can't be found, or depth exceeds
138: * MAX_RECURSION.
139: */
140: static int
141: getent(cap, len, db_array, fd, name, depth)
142: char **cap, **db_array, *name;
143: u_int *len;
144: int fd, depth;
145: {
1.4 ! mpech 146: char *r_end, *rp, **db_p;
1.1 tholo 147: int myfd, eof, foundit;
148: char *record;
149: int tc_not_resolved;
150:
151: /*
152: * Return with ``loop detected'' error if we've recursed more than
153: * MAX_RECURSION times.
154: */
155: if (depth > MAX_RECURSION)
156: return (-3);
157:
158: /*
159: * Allocate first chunk of memory.
160: */
161: if ((record = malloc(BFRAG)) == NULL) {
162: errno = ENOMEM;
163: return (-2);
164: }
165: r_end = record + BFRAG;
166: foundit = 0;
167: rp = NULL;
168: myfd = -1;
169: /*
170: * Loop through database array until finding the record.
171: */
172:
173: for (db_p = db_array; *db_p != NULL; db_p++) {
174: eof = 0;
175:
176: /*
177: * Open database if not already open.
178: */
179:
180: if (fd >= 0) {
181: (void)lseek(fd, (off_t)0, SEEK_SET);
182: myfd = 0;
183: } else {
184: fd = open(*db_p, O_RDONLY, 0);
185: if (fd < 0) {
186: /* No error on unfound file. */
187: continue;
188: }
189: myfd = 1;
190: }
191: /*
192: * Find the requested capability record ...
193: */
194: {
195: char buf[BUFSIZ];
1.4 ! mpech 196: char *b_end, *bp;
! 197: int c;
1.1 tholo 198:
199: /*
200: * Loop invariants:
201: * There is always room for one more character in record.
202: * R_end always points just past end of record.
203: * Rp always points just past last character in record.
204: * B_end always points just past last character in buf.
205: * Bp always points at next character in buf.
206: */
207: b_end = buf;
208: bp = buf;
209: for (;;) {
210:
211: /*
212: * Read in a line implementing (\, newline)
213: * line continuation.
214: */
215: rp = record;
216: for (;;) {
217: if (bp >= b_end) {
218: int n;
219:
220: n = read(fd, buf, sizeof(buf));
221: if (n <= 0) {
222: if (myfd)
223: (void)close(fd);
224: if (n < 0) {
225: free(record);
226: return (-2);
227: } else {
228: fd = -1;
229: eof = 1;
230: break;
231: }
232: }
233: b_end = buf+n;
234: bp = buf;
235: }
236:
237: c = *bp++;
238: if (c == '\n') {
239: if (bp >= b_end) {
240: int n;
241:
242: n = read(fd, buf, sizeof(buf));
243: if (n <= 0) {
244: if (myfd)
245: (void)close(fd);
246: if (n < 0) {
247: free(record);
248: return (-2);
249: } else {
250: fd = -1;
251: eof = 1;
252: break;
253: }
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: /*
267: * Enforce loop invariant: if no room
268: * left in record buffer, try to get
269: * some more.
270: */
271: if (rp >= r_end) {
272: u_int pos;
273: size_t newsize;
274:
275: pos = rp - record;
276: newsize = r_end - record + BFRAG;
277: record = realloc(record, newsize);
278: if (record == NULL) {
279: errno = ENOMEM;
280: if (myfd)
281: (void)close(fd);
282: return (-2);
283: }
284: r_end = record + newsize;
285: rp = record + pos;
286: }
287: }
288: /* loop invariant let's us do this */
289: *rp++ = '\0';
290:
291: /*
292: * Toss blank lines and comments.
293: */
294: if (*record == '\0' || *record == '#')
295: continue;
296:
297: /*
298: * See if this is the record we want ...
299: */
300: if (igetmatch(record, name) == 0) {
301: foundit = 1;
302: break; /* found it! */
303: }
1.2 naddy 304:
305: /*
306: * If encountered eof check next file.
307: */
308: if (eof)
309: break;
1.1 tholo 310: }
311: }
312: if (foundit)
313: break;
314: }
315:
316: if (!foundit)
317: return (-1);
318:
319: /*
320: * Got the capability record, but now we have to expand all use=name
321: * references in it ...
322: */
323: {
1.4 ! mpech 324: char *newicap, *s;
! 325: int newilen;
1.1 tholo 326: u_int ilen;
327: int diff, iret, tclen;
328: char *icap, *scan, *tc, *tcstart, *tcend;
329:
330: /*
331: * Loop invariants:
332: * There is room for one more character in record.
333: * R_end points just past end of record.
334: * Rp points just past last character in record.
335: * Scan points at remainder of record that needs to be
336: * scanned for use=name constructs.
337: */
338: scan = record;
339: tc_not_resolved = 0;
340: for (;;) {
341: if ((tc = igetcap(scan, "use", '=')) == NULL)
342: break;
343:
344: /*
345: * Find end of use=name and stomp on the trailing `,'
346: * (if present) so we can use it to call ourselves.
347: */
348: s = tc;
349: for (;;)
350: if (*s == '\0')
351: break;
352: else
353: if (*s++ == ',') {
354: *(s - 1) = '\0';
355: break;
356: }
357: tcstart = tc - 4;
358: tclen = s - tcstart;
359: tcend = s;
360:
361: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1);
362: newicap = icap; /* Put into a register. */
363: newilen = ilen;
364: if (iret != 0) {
365: /* an error */
366: if (iret < -1) {
367: if (myfd)
368: (void)close(fd);
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) {
376: *(s - 1) = ',';
377: scan = s - 1;
378: tc_not_resolved = 1;
379: continue;
380:
381: }
382: }
383: /* not interested in name field of tc'ed record */
384: s = newicap;
385: for (;;)
386: if (*s == '\0')
387: break;
388: else
389: if (*s++ == ',')
390: break;
391: newilen -= s - newicap;
392: newicap = s;
393:
394: /* make sure interpolated record is `,'-terminated */
395: s += newilen;
396: if (*(s-1) != ',') {
397: *s = ','; /* overwrite NUL with , */
398: newilen++;
399: }
400:
401: /*
402: * Make sure there's enough room to insert the
403: * new record.
404: */
405: diff = newilen - tclen;
406: if (diff >= r_end - rp) {
407: u_int pos, tcpos, tcposend;
408: size_t newsize;
409:
410: pos = rp - record;
411: newsize = r_end - record + diff + BFRAG;
412: tcpos = tcstart - record;
413: tcposend = tcend - record;
414: record = realloc(record, newsize);
415: if (record == NULL) {
416: errno = ENOMEM;
417: if (myfd)
418: (void)close(fd);
419: free(icap);
420: return (-2);
421: }
422: r_end = record + newsize;
423: rp = record + pos;
424: tcstart = record + tcpos;
425: tcend = record + tcposend;
426: }
427:
428: /*
429: * Insert tc'ed record into our record.
430: */
431: s = tcstart + newilen;
432: bcopy(tcend, s, (size_t)(rp - tcend));
433: bcopy(newicap, tcstart, (size_t)newilen);
434: rp += diff;
435: free(icap);
436:
437: /*
438: * Start scan on `,' so next igetcap works properly
439: * (igetcap always skips first field).
440: */
441: scan = s-1;
442: }
443:
444: }
445: /*
446: * Close file (if we opened it), give back any extra memory, and
447: * return capability, length and success.
448: */
449: if (myfd)
450: (void)close(fd);
451: *len = rp - record - 1; /* don't count NUL */
452: if (r_end > rp)
453: if ((record =
454: realloc(record, (size_t)(rp - record))) == NULL) {
455: errno = ENOMEM;
456: return (-2);
457: }
458:
459: *cap = record;
460: if (tc_not_resolved)
461: return (1);
462: return (0);
463: }
464:
465: /*
466: * Cgetmatch will return 0 if name is one of the names of the capability
467: * record buf, -1 if not.
468: */
469: static int
470: igetmatch(buf, name)
471: char *buf, *name;
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
513: igetclose()
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: /*
525: * Cgetnext() gets either the first or next entry in the logical database
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
530: igetnext(bp, db_array)
1.4 ! mpech 531: char **bp;
1.1 tholo 532: char **db_array;
533: {
534: size_t len;
535: int status, done;
536: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
537: u_int dummy;
538:
539: if (dbp == NULL)
540: dbp = db_array;
541:
542: if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
543: (void)igetclose();
544: return (-1);
545: }
546: for(;;) {
547: line = fgetln(pfp, &len);
548: if (line == NULL && pfp) {
549: (void)fclose(pfp);
550: if (ferror(pfp)) {
551: (void)igetclose();
552: return (-1);
553: } else {
554: if (*++dbp == NULL) {
555: (void)igetclose();
556: return (0);
557: } else if ((pfp =
558: fopen(*dbp, "r")) == NULL) {
559: (void)igetclose();
560: return (-1);
561: } else
562: continue;
563: }
564: } else
565: line[len - 1] = '\0';
566: if (len == 1) {
567: slash = 0;
568: continue;
569: }
570: if (isspace(*line) ||
571: *line == ',' || *line == '#' || slash) {
572: if (line[len - 2] == '\\')
573: slash = 1;
574: else
575: slash = 0;
576: continue;
577: }
578: if (line[len - 2] == '\\')
579: slash = 1;
580: else
581: slash = 0;
582:
583: /*
584: * Line points to a name line.
585: */
586: done = 0;
587: np = nbuf;
588: for (;;) {
589: for (cp = line; *cp != '\0'; cp++) {
590: if (*cp == ',') {
591: *np++ = ',';
592: done = 1;
593: break;
594: }
595: *np++ = *cp;
596: }
597: if (done) {
598: *np = '\0';
599: break;
600: } else { /* name field extends beyond the line */
601: line = fgetln(pfp, &len);
602: if (line == NULL && pfp) {
603: (void)fclose(pfp);
604: if (ferror(pfp)) {
605: (void)igetclose();
606: return (-1);
607: }
608: } else
609: line[len - 1] = '\0';
610: }
611: }
612: rp = buf;
613: for(cp = nbuf; *cp != NULL; cp++)
614: if (*cp == '|' || *cp == ',')
615: break;
616: else
617: *rp++ = *cp;
618:
619: *rp = '\0';
620: status = getent(bp, &dummy, db_array, -1, buf, 0);
621: if (status == -2 || status == -3)
622: (void)igetclose();
623:
624: return (status + 1);
625: }
626: /* NOTREACHED */
627: }