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