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