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