Annotation of src/usr.bin/cap_mkdb/getinfo.c, Revision 1.14
1.14 ! deraadt 1: /* $OpenBSD: getinfo.c,v 1.13 2007/05/07 03:53:46 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.
1.14 ! deraadt 17: * 3. The name of the author may not be used to endorse or promote products
1.1 tholo 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.14 ! deraadt 33: static char rcsid[] = "$OpenBSD: getinfo.c,v 1.13 2007/05/07 03:53:46 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;
1.12 ray 140: char *record, *s;
1.1 tholo 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;
1.12 ray 269: s = realloc(record, newsize);
270: if (s == NULL) {
271: free(record);
1.1 tholo 272: errno = ENOMEM;
273: if (myfd)
274: (void)close(fd);
275: return (-2);
276: }
1.12 ray 277: record = s;
1.1 tholo 278: r_end = record + newsize;
279: rp = record + pos;
280: }
281: }
1.11 ray 282: /* loop invariant lets us do this */
1.1 tholo 283: *rp++ = '\0';
284:
285: /*
286: * Toss blank lines and comments.
287: */
288: if (*record == '\0' || *record == '#')
289: continue;
290:
291: /*
292: * See if this is the record we want ...
293: */
294: if (igetmatch(record, name) == 0) {
295: foundit = 1;
296: break; /* found it! */
297: }
1.2 naddy 298:
299: /*
300: * If encountered eof check next file.
301: */
302: if (eof)
303: break;
1.1 tholo 304: }
305: }
306: if (foundit)
307: break;
308: }
309:
310: if (!foundit)
311: return (-1);
312:
313: /*
314: * Got the capability record, but now we have to expand all use=name
315: * references in it ...
316: */
317: {
1.12 ray 318: char *newicap;
1.4 mpech 319: int newilen;
1.1 tholo 320: u_int ilen;
321: int diff, iret, tclen;
322: char *icap, *scan, *tc, *tcstart, *tcend;
323:
324: /*
325: * Loop invariants:
326: * There is room for one more character in record.
327: * R_end points just past end of record.
328: * Rp points just past last character in record.
329: * Scan points at remainder of record that needs to be
330: * scanned for use=name constructs.
331: */
332: scan = record;
333: tc_not_resolved = 0;
334: for (;;) {
335: if ((tc = igetcap(scan, "use", '=')) == NULL)
336: break;
337:
338: /*
339: * Find end of use=name and stomp on the trailing `,'
340: * (if present) so we can use it to call ourselves.
341: */
1.12 ray 342: s = tc + strcspn(tc, ",");
343: if (*s == ',') {
344: *s = '\0';
345: ++s;
346: }
1.1 tholo 347: tcstart = tc - 4;
348: tclen = s - tcstart;
349: tcend = s;
350:
351: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1);
352: newicap = icap; /* Put into a register. */
353: newilen = ilen;
354: if (iret != 0) {
355: /* an error */
356: if (iret < -1) {
357: if (myfd)
358: (void)close(fd);
359: free(record);
360: return (iret);
361: }
362: if (iret == 1)
363: tc_not_resolved = 1;
364: /* couldn't resolve tc */
365: if (iret == -1) {
366: *(s - 1) = ',';
367: scan = s - 1;
368: tc_not_resolved = 1;
369: continue;
370:
371: }
372: }
373: /* not interested in name field of tc'ed record */
1.12 ray 374: s = newicap + strcspn(newicap, ",");
375: if (*s == ',')
376: ++s;
1.1 tholo 377: newilen -= s - newicap;
378: newicap = s;
379:
380: /* make sure interpolated record is `,'-terminated */
381: s += newilen;
382: if (*(s-1) != ',') {
383: *s = ','; /* overwrite NUL with , */
384: newilen++;
385: }
386:
387: /*
388: * Make sure there's enough room to insert the
389: * new record.
390: */
391: diff = newilen - tclen;
392: if (diff >= r_end - rp) {
393: u_int pos, tcpos, tcposend;
394: size_t newsize;
395:
396: pos = rp - record;
397: newsize = r_end - record + diff + BFRAG;
398: tcpos = tcstart - record;
399: tcposend = tcend - record;
1.12 ray 400: s = realloc(record, newsize);
401: if (s == NULL) {
402: free(record);
1.1 tholo 403: errno = ENOMEM;
404: if (myfd)
405: (void)close(fd);
406: free(icap);
407: return (-2);
408: }
1.12 ray 409: record = s;
1.1 tholo 410: r_end = record + newsize;
411: rp = record + pos;
412: tcstart = record + tcpos;
413: tcend = record + tcposend;
414: }
415:
416: /*
417: * Insert tc'ed record into our record.
418: */
419: s = tcstart + newilen;
420: bcopy(tcend, s, (size_t)(rp - tcend));
421: bcopy(newicap, tcstart, (size_t)newilen);
422: rp += diff;
423: free(icap);
424:
425: /*
426: * Start scan on `,' so next igetcap works properly
427: * (igetcap always skips first field).
428: */
429: scan = s-1;
430: }
431:
432: }
433: /*
434: * Close file (if we opened it), give back any extra memory, and
435: * return capability, length and success.
436: */
437: if (myfd)
438: (void)close(fd);
439: *len = rp - record - 1; /* don't count NUL */
1.12 ray 440: if (r_end > rp) {
441: if ((s =
1.1 tholo 442: realloc(record, (size_t)(rp - record))) == NULL) {
1.12 ray 443: free(record);
1.1 tholo 444: errno = ENOMEM;
445: return (-2);
1.12 ray 446: } else
447: record = s;
448: }
1.1 tholo 449:
450: *cap = record;
451: if (tc_not_resolved)
452: return (1);
453: return (0);
454: }
455:
456: /*
1.8 jmc 457: * Igetmatch will return 0 if name is one of the names of the capability
1.1 tholo 458: * record buf, -1 if not.
459: */
460: static int
1.7 deraadt 461: igetmatch(char *buf, char *name)
1.1 tholo 462: {
1.4 mpech 463: char *np, *bp;
1.1 tholo 464:
465: /*
466: * Start search at beginning of record.
467: */
468: bp = buf;
469: for (;;) {
470: /*
471: * Try to match a record name.
472: */
473: np = name;
474: for (;;)
475: if (*np == '\0') {
476: if (*bp == '|' || *bp == ',' || *bp == '\0')
477: return (0);
478: else
479: break;
480: } else {
481: if (*bp++ != *np++)
482: break;
483: }
484:
485: /*
486: * Match failed, skip to next name in record.
487: */
488: bp--; /* a '|' or ',' may have stopped the match */
489: for (;;)
490: if (*bp == '\0' || *bp == ',')
491: return (-1); /* match failed totally */
492: else
493: if (*bp++ == '|')
494: break; /* found next name */
495: }
496: }
497:
498: static FILE *pfp;
499: static int slash;
500: static char **dbp;
501:
502: static int
1.7 deraadt 503: igetclose(void)
1.1 tholo 504: {
505: if (pfp != NULL) {
506: (void)fclose(pfp);
507: pfp = NULL;
508: }
509: dbp = NULL;
510: slash = 0;
511: return(0);
512: }
513:
514: /*
1.8 jmc 515: * Igetnext() gets either the first or next entry in the logical database
1.1 tholo 516: * specified by db_array. It returns 0 upon completion of the database, 1
517: * upon returning an entry with more remaining, and -1 if an error occurs.
518: */
519: int
1.7 deraadt 520: igetnext(char **bp, char **db_array)
1.1 tholo 521: {
522: size_t len;
523: int status, done;
524: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
525: u_int dummy;
526:
527: if (dbp == NULL)
528: dbp = db_array;
529:
530: if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
531: (void)igetclose();
532: return (-1);
533: }
1.10 ray 534: for (;;) {
1.1 tholo 535: line = fgetln(pfp, &len);
1.10 ray 536: if (line == NULL) {
1.1 tholo 537: if (ferror(pfp)) {
538: (void)igetclose();
539: return (-1);
540: } else {
1.9 ray 541: (void)fclose(pfp);
542: pfp = NULL;
1.1 tholo 543: if (*++dbp == NULL) {
544: (void)igetclose();
545: return (0);
546: } else if ((pfp =
547: fopen(*dbp, "r")) == NULL) {
548: (void)igetclose();
549: return (-1);
550: } else
551: continue;
552: }
553: } else
1.9 ray 554: line[len - 1] = '\0';/* XXX - assumes newline */
1.1 tholo 555: if (len == 1) {
556: slash = 0;
557: continue;
558: }
559: if (isspace(*line) ||
560: *line == ',' || *line == '#' || slash) {
561: if (line[len - 2] == '\\')
562: slash = 1;
563: else
564: slash = 0;
565: continue;
566: }
567: if (line[len - 2] == '\\')
568: slash = 1;
569: else
570: slash = 0;
571:
572: /*
573: * Line points to a name line.
574: */
575: done = 0;
576: np = nbuf;
577: for (;;) {
578: for (cp = line; *cp != '\0'; cp++) {
579: if (*cp == ',') {
580: *np++ = ',';
581: done = 1;
582: break;
583: }
584: *np++ = *cp;
585: }
586: if (done) {
587: *np = '\0';
588: break;
589: } else { /* name field extends beyond the line */
590: line = fgetln(pfp, &len);
1.10 ray 591: if (line == NULL) {
1.1 tholo 592: if (ferror(pfp)) {
593: (void)igetclose();
594: return (-1);
595: }
1.9 ray 596: /* Move on to next file. */
597: (void)fclose(pfp);
598: pfp = NULL;
599: ++dbp;
600: /* NUL terminate nbuf. */
601: *np = '\0';
602: break;
1.1 tholo 603: } else
1.9 ray 604: /* XXX - assumes newline */
1.1 tholo 605: line[len - 1] = '\0';
606: }
607: }
608: rp = buf;
1.13 ray 609: for(cp = nbuf; *cp != '\0'; cp++)
1.1 tholo 610: if (*cp == '|' || *cp == ',')
611: break;
612: else
613: *rp++ = *cp;
614:
615: *rp = '\0';
616: status = getent(bp, &dummy, db_array, -1, buf, 0);
617: if (status == -2 || status == -3)
618: (void)igetclose();
619:
620: return (status + 1);
621: }
622: /* NOTREACHED */
623: }