|
|
1.52 ! joerg 1: /* $NetBSD: getcap.c,v 1.51 2012/03/13 21:13:35 christos Exp $ */
1.9 cgd 2:
1.1 cgd 3: /*-
1.9 cgd 4: * Copyright (c) 1992, 1993
5: * The Regents of the University of California. All rights reserved.
1.1 cgd 6: *
7: * This code is derived from software contributed to Berkeley by
8: * Casey Leedom of Lawrence Livermore National Laboratory.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
1.38 agc 18: * 3. Neither the name of the University nor the names of its contributors
1.1 cgd 19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: */
34:
1.39 lukem 35: #if HAVE_NBTOOL_CONFIG_H
36: #include "nbtool_config.h"
1.37 uwe 37: #endif
38:
1.13 christos 39: #include <sys/cdefs.h>
1.1 cgd 40: #if defined(LIBC_SCCS) && !defined(lint)
1.9 cgd 41: #if 0
42: static char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94";
43: #else
1.52 ! joerg 44: __RCSID("$NetBSD: getcap.c,v 1.51 2012/03/13 21:13:35 christos Exp $");
1.9 cgd 45: #endif
1.1 cgd 46: #endif /* LIBC_SCCS and not lint */
47:
1.41 christos 48: #ifndef SMALL
1.14 jtc 49: #include "namespace.h"
1.41 christos 50: #endif
1.1 cgd 51: #include <sys/types.h>
1.36 tron 52: #include <sys/param.h>
1.30 lukem 53:
54: #include <assert.h>
1.51 christos 55: #include <stddef.h>
1.1 cgd 56: #include <ctype.h>
1.41 christos 57: #ifndef SMALL
1.1 cgd 58: #include <db.h>
1.41 christos 59: #endif
1.1 cgd 60: #include <errno.h>
61: #include <fcntl.h>
62: #include <limits.h>
63: #include <stdio.h>
64: #include <stdlib.h>
65: #include <string.h>
66: #include <unistd.h>
1.14 jtc 67:
1.49 joerg 68: #if defined(__weak_alias) && !defined(SMALL)
1.32 mycroft 69: __weak_alias(cgetcap,_cgetcap)
70: __weak_alias(cgetclose,_cgetclose)
71: __weak_alias(cgetent,_cgetent)
72: __weak_alias(cgetfirst,_cgetfirst)
73: __weak_alias(cgetmatch,_cgetmatch)
74: __weak_alias(cgetnext,_cgetnext)
75: __weak_alias(cgetnum,_cgetnum)
76: __weak_alias(cgetset,_cgetset)
77: __weak_alias(cgetstr,_cgetstr)
78: __weak_alias(cgetustr,_cgetustr)
1.48 christos 79: __weak_alias(csetexpandtc,_csetexpandtc)
1.14 jtc 80: #endif
1.1 cgd 81:
82: #define BFRAG 1024
83: #define BSIZE 1024
84: #define ESC ('[' & 037) /* ASCII ESC */
85: #define MAX_RECURSION 32 /* maximum getent recursion */
86: #define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
87:
88: #define RECOK (char)0
89: #define TCERR (char)1
90: #define SHADOW (char)2
91:
92: static size_t topreclen; /* toprec length */
93: static char *toprec; /* Additional record specified by cgetset() */
94: static int gottoprec; /* Flag indicating retrieval of toprecord */
1.48 christos 95: static int expandtc = 1; /* flag to expand tc= or not */
1.1 cgd 96:
1.41 christos 97: #ifndef SMALL
1.40 christos 98: static int cdbget(DB *, char **, const char *);
1.41 christos 99: #endif
1.40 christos 100: static int getent(char **, size_t *, const char * const *, int,
101: const char *, int, char *);
102: static int nfcmp(char *, char *);
1.1 cgd 103:
104: /*
105: * Cgetset() allows the addition of a user specified buffer to be added
106: * to the database array, in effect "pushing" the buffer on top of the
107: * virtual database. 0 is returned on success, -1 on failure.
108: */
109: int
1.40 christos 110: cgetset(const char *ent)
1.1 cgd 111: {
1.27 abs 112: const char *source, *check;
113: char *dest;
114:
1.1 cgd 115: if (ent == NULL) {
1.47 jnemeth 116: if (toprec != NULL)
1.1 cgd 117: free(toprec);
118: toprec = NULL;
119: topreclen = 0;
1.47 jnemeth 120: return 0;
1.1 cgd 121: }
122: topreclen = strlen(ent);
1.47 jnemeth 123: if ((toprec = malloc(topreclen + 1)) == NULL) {
1.1 cgd 124: errno = ENOMEM;
1.47 jnemeth 125: return -1;
1.1 cgd 126: }
127: gottoprec = 0;
1.27 abs 128:
1.47 jnemeth 129: source = ent;
130: dest = toprec;
131: while (*source != '\0') { /* Strip whitespace */
1.27 abs 132: *dest++ = *source++; /* Do not check first field */
133: while (*source == ':') {
1.47 jnemeth 134: check = source + 1;
1.29 abs 135: while (*check && (isspace((unsigned char)*check) ||
136: (*check=='\\' && isspace((unsigned char)check[1]))))
1.27 abs 137: ++check;
1.47 jnemeth 138: if (*check == ':')
139: source = check;
1.27 abs 140: else
141: break;
142:
143: }
144: }
1.47 jnemeth 145: *dest = 0;
1.27 abs 146:
1.47 jnemeth 147: return 0;
1.1 cgd 148: }
149:
150: /*
151: * Cgetcap searches the capability record buf for the capability cap with
152: * type `type'. A pointer to the value of cap is returned on success, NULL
153: * if the requested capability couldn't be found.
154: *
155: * Specifying a type of ':' means that nothing should follow cap (:cap:).
156: * In this case a pointer to the terminating ':' or NUL will be returned if
157: * cap is found.
158: *
159: * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
160: * return NULL.
161: */
162: char *
1.52 ! joerg 163: cgetcap(char *buf, const char *cap, int type)
1.1 cgd 164: {
1.21 mycroft 165: char *bp;
166: const char *cp;
1.1 cgd 167:
1.30 lukem 168: _DIAGASSERT(buf != NULL);
169: _DIAGASSERT(cap != NULL);
170:
1.1 cgd 171: bp = buf;
172: for (;;) {
173: /*
174: * Skip past the current capability field - it's either the
175: * name field if this is the first time through the loop, or
176: * the remainder of a field whose name failed to match cap.
177: */
178: for (;;)
179: if (*bp == '\0')
1.47 jnemeth 180: return NULL;
181: else if (*bp++ == ':')
182: break;
1.1 cgd 183:
184: /*
185: * Try to match (cap, type) in buf.
186: */
187: for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
188: continue;
189: if (*cp != '\0')
190: continue;
191: if (*bp == '@')
1.47 jnemeth 192: return NULL;
1.1 cgd 193: if (type == ':') {
194: if (*bp != '\0' && *bp != ':')
195: continue;
1.47 jnemeth 196: return bp;
1.1 cgd 197: }
198: if (*bp != type)
199: continue;
200: bp++;
1.47 jnemeth 201: return *bp == '@' ? NULL : bp;
1.1 cgd 202: }
203: /* NOTREACHED */
204: }
205:
206: /*
207: * Cgetent extracts the capability record name from the NULL terminated file
208: * array db_array and returns a pointer to a malloc'd copy of it in buf.
209: * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
210: * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success,
211: * -1 if the requested record couldn't be found, -2 if a system error was
212: * encountered (couldn't open/read a file, etc.), and -3 if a potential
213: * reference loop is detected.
214: */
1.45 christos 215: /* coverity[+alloc : arg-*0] */
1.1 cgd 216: int
1.40 christos 217: cgetent(char **buf, const char * const *db_array, const char *name)
1.1 cgd 218: {
1.18 thorpej 219: size_t dummy;
1.1 cgd 220:
1.30 lukem 221: _DIAGASSERT(buf != NULL);
222: _DIAGASSERT(db_array != NULL);
223: _DIAGASSERT(name != NULL);
224:
1.47 jnemeth 225: return getent(buf, &dummy, db_array, -1, name, 0, NULL);
1.1 cgd 226: }
227:
1.48 christos 228: void
229: csetexpandtc(int etc)
230: {
231: expandtc = etc;
232: }
233:
1.1 cgd 234: /*
235: * Getent implements the functions of cgetent. If fd is non-negative,
236: * *db_array has already been opened and fd is the open file descriptor. We
237: * do this to save time and avoid using up file descriptors for tc=
238: * recursions.
239: *
240: * Getent returns the same success/failure codes as cgetent. On success, a
241: * pointer to a malloc'ed capability record with all tc= capabilities fully
242: * expanded and its length (not including trailing ASCII NUL) are left in
243: * *cap and *len.
244: *
245: * Basic algorithm:
246: * + Allocate memory incrementally as needed in chunks of size BFRAG
247: * for capability buffer.
248: * + Recurse for each tc=name and interpolate result. Stop when all
249: * names interpolated, a name can't be found, or depth exceeds
250: * MAX_RECURSION.
251: */
1.45 christos 252: /* coverity[+alloc : arg-*0] */
1.1 cgd 253: static int
1.40 christos 254: getent(char **cap, size_t *len, const char * const *db_array, int fd,
255: const char *name, int depth, char *nfield)
1.1 cgd 256: {
1.41 christos 257: #ifndef SMALL
1.1 cgd 258: DB *capdbp;
1.41 christos 259: char pbuf[MAXPATHLEN];
260: char *cbuf;
261: int retval;
262: size_t clen;
263: #endif
264: char *record, *newrecord;
1.47 jnemeth 265: char *r_end, *rp; /* pacify gcc */
1.40 christos 266: const char * const *db_p;
1.47 jnemeth 267: int myfd, eof, foundit;
1.1 cgd 268: int tc_not_resolved;
269:
1.30 lukem 270: _DIAGASSERT(cap != NULL);
271: _DIAGASSERT(len != NULL);
272: _DIAGASSERT(db_array != NULL);
273: /* fd may be -1 */
274: _DIAGASSERT(name != NULL);
275: /* nfield may be NULL */
276:
1.47 jnemeth 277: myfd = 0;
278: rp = NULL;
279:
1.1 cgd 280: /*
281: * Return with ``loop detected'' error if we've recursed more than
282: * MAX_RECURSION times.
283: */
284: if (depth > MAX_RECURSION)
1.47 jnemeth 285: return -3;
1.1 cgd 286:
287: /*
288: * Check if we have a top record from cgetset().
289: */
290: if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
1.47 jnemeth 291: if ((record = malloc(topreclen + BFRAG)) == NULL) {
1.1 cgd 292: errno = ENOMEM;
1.47 jnemeth 293: return -2;
1.1 cgd 294: }
1.11 mrg 295: (void)strcpy(record, toprec); /* XXX: strcpy is safe */
1.1 cgd 296: db_p = db_array;
297: rp = record + topreclen + 1;
298: r_end = rp + BFRAG;
299: goto tc_exp;
300: }
301: /*
302: * Allocate first chunk of memory.
303: */
304: if ((record = malloc(BFRAG)) == NULL) {
305: errno = ENOMEM;
1.47 jnemeth 306: return -2;
1.1 cgd 307: }
308: r_end = record + BFRAG;
309: foundit = 0;
310: /*
311: * Loop through database array until finding the record.
312: */
313:
314: for (db_p = db_array; *db_p != NULL; db_p++) {
315: eof = 0;
316:
317: /*
318: * Open database if not already open.
319: */
320:
321: if (fd >= 0) {
1.15 kleink 322: (void)lseek(fd, (off_t)0, SEEK_SET);
1.1 cgd 323: } else {
1.41 christos 324: #ifndef SMALL
1.1 cgd 325: (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
1.48 christos 326: if (expandtc &&
327: (capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
1.1 cgd 328: != NULL) {
329: free(record);
330: retval = cdbget(capdbp, &record, name);
1.8 cgd 331: if (retval < 0) {
332: /* no record available */
333: (void)capdbp->close(capdbp);
1.47 jnemeth 334: return retval;
1.8 cgd 335: }
336: /* save the data; close frees it */
1.7 cgd 337: clen = strlen(record);
1.46 jnemeth 338: if ((cbuf = malloc(clen + 1)) == NULL) {
339: (void)capdbp->close(capdbp);
340: errno = ENOMEM;
1.47 jnemeth 341: return -2;
1.46 jnemeth 342: }
1.23 perry 343: memmove(cbuf, record, clen + 1);
1.7 cgd 344: if (capdbp->close(capdbp) < 0) {
1.30 lukem 345: int serrno = errno;
346:
1.7 cgd 347: free(cbuf);
1.30 lukem 348: errno = serrno;
1.47 jnemeth 349: return -2;
1.7 cgd 350: }
351: *len = clen;
352: *cap = cbuf;
1.47 jnemeth 353: return retval;
1.41 christos 354: } else
355: #endif
356: {
1.1 cgd 357: fd = open(*db_p, O_RDONLY, 0);
358: if (fd < 0) {
359: /* No error on unfound file. */
1.10 mycroft 360: continue;
1.1 cgd 361: }
362: myfd = 1;
363: }
364: }
365: /*
366: * Find the requested capability record ...
367: */
368: {
369: char buf[BUFSIZ];
1.20 mycroft 370: char *b_end, *bp, *cp;
371: int c, slash;
1.1 cgd 372:
373: /*
374: * Loop invariants:
375: * There is always room for one more character in record.
376: * R_end always points just past end of record.
377: * Rp always points just past last character in record.
378: * B_end always points just past last character in buf.
379: * Bp always points at next character in buf.
1.20 mycroft 380: * Cp remembers where the last colon was.
1.1 cgd 381: */
382: b_end = buf;
383: bp = buf;
1.47 jnemeth 384: cp = NULL;
1.20 mycroft 385: slash = 0;
1.1 cgd 386: for (;;) {
387: /*
388: * Read in a line implementing (\, newline)
389: * line continuation.
390: */
391: rp = record;
392: for (;;) {
393: if (bp >= b_end) {
1.51 christos 394: ssize_t n;
1.1 cgd 395:
396: n = read(fd, buf, sizeof(buf));
397: if (n <= 0) {
398: if (myfd)
399: (void)close(fd);
400: if (n < 0) {
1.30 lukem 401: int serrno = errno;
402:
1.1 cgd 403: free(record);
1.30 lukem 404: errno = serrno;
1.47 jnemeth 405: return -2;
1.1 cgd 406: } else {
407: fd = -1;
408: eof = 1;
409: break;
410: }
411: }
412: b_end = buf+n;
413: bp = buf;
414: }
415:
416: c = *bp++;
417: if (c == '\n') {
1.20 mycroft 418: if (slash) {
419: slash = 0;
1.1 cgd 420: rp--;
421: continue;
422: } else
423: break;
424: }
1.20 mycroft 425: if (slash) {
426: slash = 0;
427: cp = 0;
428: }
429: if (c == ':') {
430: /*
431: * If the field was `empty' (i.e.
432: * contained only white space), back up
433: * to the colon (eliminating the
434: * field).
435: */
1.47 jnemeth 436: if (cp != NULL)
1.20 mycroft 437: rp = cp;
438: else
439: cp = rp;
440: } else if (c == '\\') {
441: slash = 1;
442: } else if (c != ' ' && c != '\t') {
443: /*
444: * Forget where the colon was, as this
445: * is not an empty field.
446: */
447: cp = 0;
448: }
1.1 cgd 449: *rp++ = c;
450:
451: /*
452: * Enforce loop invariant: if no room
453: * left in record buffer, try to get
454: * some more.
455: */
456: if (rp >= r_end) {
1.51 christos 457: ptrdiff_t pos;
1.1 cgd 458: size_t newsize;
459:
460: pos = rp - record;
461: newsize = r_end - record + BFRAG;
1.33 itojun 462: newrecord = realloc(record, newsize);
463: if (newrecord == NULL) {
464: free(record);
1.1 cgd 465: if (myfd)
466: (void)close(fd);
1.30 lukem 467: errno = ENOMEM;
1.47 jnemeth 468: return -2;
1.1 cgd 469: }
1.33 itojun 470: record = newrecord;
1.1 cgd 471: r_end = record + newsize;
472: rp = record + pos;
473: }
474: }
1.20 mycroft 475: /* Eliminate any white space after the last colon. */
476: if (cp)
477: rp = cp + 1;
478: /* Loop invariant lets us do this. */
1.1 cgd 479: *rp++ = '\0';
480:
481: /*
482: * If encountered eof check next file.
483: */
484: if (eof)
485: break;
486:
487: /*
488: * Toss blank lines and comments.
489: */
490: if (*record == '\0' || *record == '#')
491: continue;
492:
493: /*
494: * See if this is the record we want ...
495: */
1.47 jnemeth 496: if (cgetmatch(record, name) == 0)
1.1 cgd 497: if (nfield == NULL || !nfcmp(nfield, record)) {
498: foundit = 1;
499: break; /* found it! */
500: }
501: }
1.47 jnemeth 502: }
1.1 cgd 503: if (foundit)
504: break;
505: }
506:
507: if (!foundit)
1.47 jnemeth 508: return -1;
1.1 cgd 509:
510: /*
511: * Got the capability record, but now we have to expand all tc=name
512: * references in it ...
513: */
1.48 christos 514: tc_exp:
515: tc_not_resolved = 0;
516: if (expandtc) {
1.16 perry 517: char *newicap, *s;
1.17 perry 518: size_t ilen, newilen;
1.51 christos 519: int iret;
520: ptrdiff_t diff, tclen;
1.1 cgd 521: char *icap, *scan, *tc, *tcstart, *tcend;
522:
523: /*
524: * Loop invariants:
525: * There is room for one more character in record.
526: * R_end points just past end of record.
527: * Rp points just past last character in record.
528: * Scan points at remainder of record that needs to be
529: * scanned for tc=name constructs.
530: */
531: scan = record;
532: for (;;) {
533: if ((tc = cgetcap(scan, "tc", '=')) == NULL)
534: break;
535:
536: /*
537: * Find end of tc=name and stomp on the trailing `:'
538: * (if present) so we can use it to call ourselves.
539: */
540: s = tc;
541: for (;;)
542: if (*s == '\0')
543: break;
544: else
545: if (*s++ == ':') {
546: *(s - 1) = '\0';
547: break;
548: }
549: tcstart = tc - 3;
550: tclen = s - tcstart;
551: tcend = s;
552:
553: iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
554: NULL);
555: newicap = icap; /* Put into a register. */
556: newilen = ilen;
557: if (iret != 0) {
558: /* an error */
559: if (iret < -1) {
560: if (myfd)
561: (void)close(fd);
562: free(record);
1.47 jnemeth 563: return iret;
1.1 cgd 564: }
565: if (iret == 1)
566: tc_not_resolved = 1;
567: /* couldn't resolve tc */
568: if (iret == -1) {
569: *(s - 1) = ':';
570: scan = s - 1;
571: tc_not_resolved = 1;
572: continue;
573:
574: }
575: }
576: /* not interested in name field of tc'ed record */
577: s = newicap;
578: for (;;)
579: if (*s == '\0')
580: break;
1.47 jnemeth 581: else if (*s++ == ':')
582: break;
1.1 cgd 583: newilen -= s - newicap;
584: newicap = s;
585:
586: /* make sure interpolated record is `:'-terminated */
587: s += newilen;
1.47 jnemeth 588: if (*(s - 1) != ':') {
1.1 cgd 589: *s = ':'; /* overwrite NUL with : */
590: newilen++;
591: }
592:
593: /*
594: * Make sure there's enough room to insert the
595: * new record.
596: */
597: diff = newilen - tclen;
598: if (diff >= r_end - rp) {
1.51 christos 599: ptrdiff_t pos, tcpos, tcposend;
1.1 cgd 600: size_t newsize;
601:
602: pos = rp - record;
603: newsize = r_end - record + diff + BFRAG;
604: tcpos = tcstart - record;
605: tcposend = tcend - record;
1.33 itojun 606: newrecord = realloc(record, newsize);
607: if (newrecord == NULL) {
608: free(record);
1.1 cgd 609: if (myfd)
610: (void)close(fd);
611: free(icap);
1.30 lukem 612: errno = ENOMEM;
1.47 jnemeth 613: return -2;
1.1 cgd 614: }
1.33 itojun 615: record = newrecord;
1.1 cgd 616: r_end = record + newsize;
617: rp = record + pos;
618: tcstart = record + tcpos;
619: tcend = record + tcposend;
620: }
621:
622: /*
623: * Insert tc'ed record into our record.
624: */
625: s = tcstart + newilen;
1.23 perry 626: memmove(s, tcend, (size_t)(rp - tcend));
627: memmove(tcstart, newicap, newilen);
1.1 cgd 628: rp += diff;
629: free(icap);
630:
631: /*
632: * Start scan on `:' so next cgetcap works properly
633: * (cgetcap always skips first field).
634: */
1.47 jnemeth 635: scan = s - 1;
1.1 cgd 636: }
637:
638: }
639: /*
640: * Close file (if we opened it), give back any extra memory, and
641: * return capability, length and success.
642: */
643: if (myfd)
644: (void)close(fd);
645: *len = rp - record - 1; /* don't count NUL */
1.33 itojun 646: if (r_end > rp) {
647: if ((newrecord =
1.1 cgd 648: realloc(record, (size_t)(rp - record))) == NULL) {
1.33 itojun 649: free(record);
1.1 cgd 650: errno = ENOMEM;
1.47 jnemeth 651: return -2;
1.1 cgd 652: }
1.33 itojun 653: record = newrecord;
654: }
1.1 cgd 655:
656: *cap = record;
657: if (tc_not_resolved)
1.47 jnemeth 658: return 1;
659: return 0;
1.1 cgd 660: }
661:
1.41 christos 662: #ifndef SMALL
1.1 cgd 663: static int
1.40 christos 664: cdbget(DB *capdbp, char **bp, const char *name)
1.1 cgd 665: {
1.25 christos 666: DBT key;
1.24 christos 667: DBT data;
1.1 cgd 668:
1.30 lukem 669: _DIAGASSERT(capdbp != NULL);
670: _DIAGASSERT(bp != NULL);
671: _DIAGASSERT(name != NULL);
672:
1.42 christos 673: key.data = __UNCONST(name);
1.1 cgd 674: key.size = strlen(name);
675:
676: for (;;) {
677: /* Get the reference. */
678: switch(capdbp->get(capdbp, &key, &data, 0)) {
679: case -1:
1.47 jnemeth 680: return -2;
1.1 cgd 681: case 1:
1.47 jnemeth 682: return -1;
1.1 cgd 683: }
684:
685: /* If not an index to another record, leave. */
686: if (((char *)data.data)[0] != SHADOW)
687: break;
688:
689: key.data = (char *)data.data + 1;
690: key.size = data.size - 1;
691: }
692:
693: *bp = (char *)data.data + 1;
1.47 jnemeth 694: return ((char *)(data.data))[0] == TCERR ? 1 : 0;
1.1 cgd 695: }
1.41 christos 696: #endif
1.1 cgd 697:
698: /*
699: * Cgetmatch will return 0 if name is one of the names of the capability
700: * record buf, -1 if not.
701: */
702: int
1.40 christos 703: cgetmatch(const char *buf, const char *name)
1.1 cgd 704: {
1.21 mycroft 705: const char *np, *bp;
1.1 cgd 706:
1.30 lukem 707: _DIAGASSERT(buf != NULL);
708: _DIAGASSERT(name != NULL);
709:
1.1 cgd 710: /*
711: * Start search at beginning of record.
712: */
713: bp = buf;
714: for (;;) {
715: /*
716: * Try to match a record name.
717: */
718: np = name;
719: for (;;)
1.26 christos 720: if (*np == '\0') {
1.1 cgd 721: if (*bp == '|' || *bp == ':' || *bp == '\0')
1.47 jnemeth 722: return 0;
1.1 cgd 723: else
724: break;
1.47 jnemeth 725: } else if (*bp++ != *np++)
726: break;
1.1 cgd 727:
728: /*
729: * Match failed, skip to next name in record.
730: */
1.34 mrg 731: if (bp > buf)
732: bp--; /* a '|' or ':' may have stopped the match */
733: else
1.47 jnemeth 734: return -1;
1.1 cgd 735: for (;;)
736: if (*bp == '\0' || *bp == ':')
1.47 jnemeth 737: return -1; /* match failed totally */
738: else if (*bp++ == '|')
739: break; /* found next name */
1.1 cgd 740: }
741: }
742:
743: int
1.40 christos 744: cgetfirst(char **buf, const char * const *db_array)
1.1 cgd 745: {
1.30 lukem 746:
747: _DIAGASSERT(buf != NULL);
748: _DIAGASSERT(db_array != NULL);
749:
1.1 cgd 750: (void)cgetclose();
1.47 jnemeth 751: return cgetnext(buf, db_array);
1.1 cgd 752: }
753:
754: static FILE *pfp;
755: static int slash;
1.40 christos 756: static const char * const *dbp;
1.1 cgd 757:
758: int
1.40 christos 759: cgetclose(void)
1.1 cgd 760: {
761: if (pfp != NULL) {
762: (void)fclose(pfp);
763: pfp = NULL;
764: }
765: dbp = NULL;
766: gottoprec = 0;
767: slash = 0;
1.47 jnemeth 768: return 0;
1.1 cgd 769: }
770:
771: /*
772: * Cgetnext() gets either the first or next entry in the logical database
773: * specified by db_array. It returns 0 upon completion of the database, 1
774: * upon returning an entry with more remaining, and -1 if an error occurs.
775: */
1.45 christos 776: /* coverity[+alloc : arg-*0] */
1.1 cgd 777: int
1.40 christos 778: cgetnext(char **bp, const char * const *db_array)
1.1 cgd 779: {
1.43 christos 780: size_t len = 0;
1.17 perry 781: int status, done;
1.1 cgd 782: char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
1.18 thorpej 783: size_t dummy;
1.1 cgd 784:
1.30 lukem 785: _DIAGASSERT(bp != NULL);
786: _DIAGASSERT(db_array != NULL);
787:
1.1 cgd 788: if (dbp == NULL)
789: dbp = db_array;
790:
1.50 christos 791: if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) {
1.1 cgd 792: (void)cgetclose();
1.47 jnemeth 793: return -1;
1.1 cgd 794: }
1.47 jnemeth 795: for (;;) {
796: if (toprec != NULL && !gottoprec) {
1.1 cgd 797: gottoprec = 1;
798: line = toprec;
799: } else {
1.6 cgd 800: line = fgetln(pfp, &len);
1.44 christos 801: if (line == NULL) {
802: if (pfp == NULL)
803: return -1;
1.1 cgd 804: if (ferror(pfp)) {
805: (void)cgetclose();
1.47 jnemeth 806: return -1;
1.1 cgd 807: } else {
1.19 tv 808: (void)fclose(pfp);
809: pfp = NULL;
1.1 cgd 810: if (*++dbp == NULL) {
811: (void)cgetclose();
1.47 jnemeth 812: return 0;
1.1 cgd 813: } else if ((pfp =
1.50 christos 814: fopen(*dbp, "re")) == NULL) {
1.1 cgd 815: (void)cgetclose();
1.47 jnemeth 816: return -1;
1.1 cgd 817: } else
818: continue;
819: }
1.5 cgd 820: } else
821: line[len - 1] = '\0';
822: if (len == 1) {
1.1 cgd 823: slash = 0;
824: continue;
825: }
1.26 christos 826: if (isspace((unsigned char)*line) ||
1.1 cgd 827: *line == ':' || *line == '#' || slash) {
1.5 cgd 828: if (line[len - 2] == '\\')
1.1 cgd 829: slash = 1;
830: else
831: slash = 0;
832: continue;
833: }
1.5 cgd 834: if (line[len - 2] == '\\')
1.1 cgd 835: slash = 1;
836: else
837: slash = 0;
838: }
839:
840:
841: /*
842: * Line points to a name line.
843: */
1.35 groo 844: if (len > sizeof(nbuf))
845: return -1;
1.1 cgd 846: done = 0;
847: np = nbuf;
848: for (;;) {
849: for (cp = line; *cp != '\0'; cp++) {
850: if (*cp == ':') {
851: *np++ = ':';
852: done = 1;
853: break;
854: }
855: if (*cp == '\\')
856: break;
857: *np++ = *cp;
858: }
859: if (done) {
860: *np = '\0';
861: break;
862: } else { /* name field extends beyond the line */
1.6 cgd 863: line = fgetln(pfp, &len);
1.1 cgd 864: if (line == NULL && pfp) {
865: if (ferror(pfp)) {
866: (void)cgetclose();
1.47 jnemeth 867: return -1;
1.1 cgd 868: }
1.19 tv 869: (void)fclose(pfp);
870: pfp = NULL;
871: *np = '\0';
872: break;
1.5 cgd 873: } else
874: line[len - 1] = '\0';
1.1 cgd 875: }
876: }
1.35 groo 877: if (len > sizeof(buf))
878: return -1;
1.1 cgd 879: rp = buf;
1.47 jnemeth 880: for (cp = nbuf; *cp != '\0'; cp++)
1.1 cgd 881: if (*cp == '|' || *cp == ':')
882: break;
883: else
884: *rp++ = *cp;
885:
886: *rp = '\0';
887: /*
888: * XXX
889: * Last argument of getent here should be nbuf if we want true
890: * sequential access in the case of duplicates.
891: * With NULL, getent will return the first entry found
892: * rather than the duplicate entry record. This is a
893: * matter of semantics that should be resolved.
894: */
895: status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
896: if (status == -2 || status == -3)
897: (void)cgetclose();
898:
1.47 jnemeth 899: return status + 1;
1.1 cgd 900: }
901: /* NOTREACHED */
902: }
903:
904: /*
905: * Cgetstr retrieves the value of the string capability cap from the
906: * capability record pointed to by buf. A pointer to a decoded, NUL
907: * terminated, malloc'd copy of the string is returned in the char *
908: * pointed to by str. The length of the string not including the trailing
909: * NUL is returned on success, -1 if the requested string capability
910: * couldn't be found, -2 if a system error was encountered (storage
911: * allocation failure).
912: */
913: int
1.40 christos 914: cgetstr(char *buf, const char *cap, char **str)
1.1 cgd 915: {
1.16 perry 916: u_int m_room;
1.21 mycroft 917: const char *bp;
918: char *mp;
1.51 christos 919: ptrdiff_t len;
1.33 itojun 920: char *mem, *newmem;
1.1 cgd 921:
1.30 lukem 922: _DIAGASSERT(buf != NULL);
923: _DIAGASSERT(cap != NULL);
924: _DIAGASSERT(str != NULL);
925:
1.1 cgd 926: /*
927: * Find string capability cap
928: */
929: bp = cgetcap(buf, cap, '=');
930: if (bp == NULL)
1.47 jnemeth 931: return -1;
1.1 cgd 932:
933: /*
934: * Conversion / storage allocation loop ... Allocate memory in
935: * chunks SFRAG in size.
936: */
937: if ((mem = malloc(SFRAG)) == NULL) {
938: errno = ENOMEM;
1.47 jnemeth 939: return -2; /* couldn't even allocate the first fragment */
1.1 cgd 940: }
941: m_room = SFRAG;
942: mp = mem;
943:
944: while (*bp != ':' && *bp != '\0') {
945: /*
946: * Loop invariants:
947: * There is always room for one more character in mem.
948: * Mp always points just past last character in mem.
949: * Bp always points at next character in buf.
950: */
951: if (*bp == '^') {
952: bp++;
953: if (*bp == ':' || *bp == '\0')
954: break; /* drop unfinished escape */
955: *mp++ = *bp++ & 037;
956: } else if (*bp == '\\') {
957: bp++;
958: if (*bp == ':' || *bp == '\0')
959: break; /* drop unfinished escape */
960: if ('0' <= *bp && *bp <= '7') {
1.16 perry 961: int n, i;
1.1 cgd 962:
963: n = 0;
964: i = 3; /* maximum of three octal digits */
965: do {
966: n = n * 8 + (*bp++ - '0');
967: } while (--i && '0' <= *bp && *bp <= '7');
968: *mp++ = n;
969: }
970: else switch (*bp++) {
971: case 'b': case 'B':
972: *mp++ = '\b';
973: break;
974: case 't': case 'T':
975: *mp++ = '\t';
976: break;
977: case 'n': case 'N':
978: *mp++ = '\n';
979: break;
980: case 'f': case 'F':
981: *mp++ = '\f';
982: break;
983: case 'r': case 'R':
984: *mp++ = '\r';
985: break;
986: case 'e': case 'E':
987: *mp++ = ESC;
988: break;
989: case 'c': case 'C':
990: *mp++ = ':';
991: break;
992: default:
993: /*
994: * Catches '\', '^', and
995: * everything else.
996: */
997: *mp++ = *(bp-1);
998: break;
999: }
1000: } else
1001: *mp++ = *bp++;
1002: m_room--;
1003:
1004: /*
1005: * Enforce loop invariant: if no room left in current
1006: * buffer, try to get some more.
1007: */
1008: if (m_room == 0) {
1009: size_t size = mp - mem;
1010:
1.33 itojun 1011: if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
1012: free(mem);
1.47 jnemeth 1013: return -2;
1.33 itojun 1014: }
1015: mem = newmem;
1.1 cgd 1016: m_room = SFRAG;
1017: mp = mem + size;
1018: }
1019: }
1020: *mp++ = '\0'; /* loop invariant let's us do this */
1021: m_room--;
1022: len = mp - mem - 1;
1023:
1024: /*
1025: * Give back any extra memory and return value and success.
1026: */
1.33 itojun 1027: if (m_room != 0) {
1028: if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
1029: free(mem);
1.47 jnemeth 1030: return -2;
1.33 itojun 1031: }
1032: mem = newmem;
1033: }
1.1 cgd 1034: *str = mem;
1.51 christos 1035: _DIAGASSERT(__type_fit(int, len));
1036: return (int)len;
1.1 cgd 1037: }
1038:
1039: /*
1040: * Cgetustr retrieves the value of the string capability cap from the
1041: * capability record pointed to by buf. The difference between cgetustr()
1042: * and cgetstr() is that cgetustr does not decode escapes but rather treats
1043: * all characters literally. A pointer to a NUL terminated malloc'd
1044: * copy of the string is returned in the char pointed to by str. The
1045: * length of the string not including the trailing NUL is returned on success,
1046: * -1 if the requested string capability couldn't be found, -2 if a system
1047: * error was encountered (storage allocation failure).
1048: */
1049: int
1.40 christos 1050: cgetustr(char *buf, const char *cap, char **str)
1.1 cgd 1051: {
1.16 perry 1052: u_int m_room;
1.21 mycroft 1053: const char *bp;
1054: char *mp;
1.51 christos 1055: size_t len;
1.33 itojun 1056: char *mem, *newmem;
1.1 cgd 1057:
1.30 lukem 1058: _DIAGASSERT(buf != NULL);
1059: _DIAGASSERT(cap != NULL);
1060: _DIAGASSERT(str != NULL);
1061:
1.1 cgd 1062: /*
1063: * Find string capability cap
1064: */
1065: if ((bp = cgetcap(buf, cap, '=')) == NULL)
1.47 jnemeth 1066: return -1;
1.1 cgd 1067:
1068: /*
1069: * Conversion / storage allocation loop ... Allocate memory in
1070: * chunks SFRAG in size.
1071: */
1072: if ((mem = malloc(SFRAG)) == NULL) {
1073: errno = ENOMEM;
1.47 jnemeth 1074: return -2; /* couldn't even allocate the first fragment */
1.1 cgd 1075: }
1076: m_room = SFRAG;
1077: mp = mem;
1078:
1079: while (*bp != ':' && *bp != '\0') {
1080: /*
1081: * Loop invariants:
1082: * There is always room for one more character in mem.
1083: * Mp always points just past last character in mem.
1084: * Bp always points at next character in buf.
1085: */
1086: *mp++ = *bp++;
1087: m_room--;
1088:
1089: /*
1090: * Enforce loop invariant: if no room left in current
1091: * buffer, try to get some more.
1092: */
1093: if (m_room == 0) {
1094: size_t size = mp - mem;
1095:
1.33 itojun 1096: if ((newmem = realloc(mem, size + SFRAG)) == NULL) {
1097: free(mem);
1.47 jnemeth 1098: return -2;
1.33 itojun 1099: }
1100: mem = newmem;
1.1 cgd 1101: m_room = SFRAG;
1102: mp = mem + size;
1103: }
1104: }
1105: *mp++ = '\0'; /* loop invariant let's us do this */
1106: m_room--;
1107: len = mp - mem - 1;
1108:
1109: /*
1110: * Give back any extra memory and return value and success.
1111: */
1.33 itojun 1112: if (m_room != 0) {
1113: if ((newmem = realloc(mem, (size_t)(mp - mem))) == NULL) {
1114: free(mem);
1.47 jnemeth 1115: return -2;
1.33 itojun 1116: }
1117: mem = newmem;
1118: }
1.1 cgd 1119: *str = mem;
1.51 christos 1120: _DIAGASSERT(__type_fit(int, len));
1121: return (int)len;
1.1 cgd 1122: }
1123:
1124: /*
1125: * Cgetnum retrieves the value of the numeric capability cap from the
1126: * capability record pointed to by buf. The numeric value is returned in
1127: * the long pointed to by num. 0 is returned on success, -1 if the requested
1128: * numeric capability couldn't be found.
1129: */
1130: int
1.40 christos 1131: cgetnum(char *buf, const char *cap, long *num)
1.1 cgd 1132: {
1.16 perry 1133: long n;
1134: int base, digit;
1.21 mycroft 1135: const char *bp;
1.1 cgd 1136:
1.30 lukem 1137: _DIAGASSERT(buf != NULL);
1138: _DIAGASSERT(cap != NULL);
1139: _DIAGASSERT(num != NULL);
1140:
1.1 cgd 1141: /*
1142: * Find numeric capability cap
1143: */
1144: bp = cgetcap(buf, cap, '#');
1145: if (bp == NULL)
1.47 jnemeth 1146: return -1;
1.1 cgd 1147:
1148: /*
1149: * Look at value and determine numeric base:
1150: * 0x... or 0X... hexadecimal,
1151: * else 0... octal,
1152: * else decimal.
1153: */
1154: if (*bp == '0') {
1155: bp++;
1156: if (*bp == 'x' || *bp == 'X') {
1157: bp++;
1158: base = 16;
1159: } else
1160: base = 8;
1161: } else
1162: base = 10;
1163:
1164: /*
1165: * Conversion loop ...
1166: */
1167: n = 0;
1168: for (;;) {
1169: if ('0' <= *bp && *bp <= '9')
1170: digit = *bp - '0';
1171: else if ('a' <= *bp && *bp <= 'f')
1172: digit = 10 + *bp - 'a';
1173: else if ('A' <= *bp && *bp <= 'F')
1174: digit = 10 + *bp - 'A';
1175: else
1176: break;
1177:
1178: if (digit >= base)
1179: break;
1180:
1181: n = n * base + digit;
1182: bp++;
1183: }
1184:
1185: /*
1186: * Return value and success.
1187: */
1188: *num = n;
1.47 jnemeth 1189: return 0;
1.1 cgd 1190: }
1191:
1192:
1193: /*
1194: * Compare name field of record.
1195: */
1196: static int
1.40 christos 1197: nfcmp(char *nf, char *rec)
1.1 cgd 1198: {
1199: char *cp, tmp;
1200: int ret;
1.30 lukem 1201:
1202: _DIAGASSERT(nf != NULL);
1203: _DIAGASSERT(rec != NULL);
1204:
1.1 cgd 1205: for (cp = rec; *cp != ':'; cp++)
1.47 jnemeth 1206: continue;
1.1 cgd 1207:
1208: tmp = *(cp + 1);
1209: *(cp + 1) = '\0';
1210: ret = strcmp(nf, rec);
1211: *(cp + 1) = tmp;
1212:
1.47 jnemeth 1213: return ret;
1.1 cgd 1214: }