11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1992, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Casey Leedom of Lawrence Livermore National Laboratory.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 4. Neither the name of the University nor the names of its contributors
171573Srgrimes *    may be used to endorse or promote products derived from this software
181573Srgrimes *    without specific prior written permission.
191573Srgrimes *
201573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301573Srgrimes * SUCH DAMAGE.
311573Srgrimes */
321573Srgrimes
331573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
341573Srgrimesstatic char sccsid[] = "@(#)getcap.c	8.3 (Berkeley) 3/25/94";
351573Srgrimes#endif /* LIBC_SCCS and not lint */
3690039Sobrien#include <sys/cdefs.h>
3790039Sobrien__FBSDID("$FreeBSD$");
381573Srgrimes
3971579Sdeischen#include "namespace.h"
401573Srgrimes#include <sys/types.h>
411573Srgrimes
421573Srgrimes#include <ctype.h>
438870Srgrimes#include <errno.h>
441573Srgrimes#include <fcntl.h>
451573Srgrimes#include <limits.h>
461573Srgrimes#include <stdio.h>
471573Srgrimes#include <stdlib.h>
481573Srgrimes#include <string.h>
491573Srgrimes#include <unistd.h>
5071579Sdeischen#include "un-namespace.h"
511573Srgrimes
5271579Sdeischen#include <db.h>
5371579Sdeischen
541573Srgrimes#define	BFRAG		1024
551573Srgrimes#define	BSIZE		1024
561573Srgrimes#define	ESC		('[' & 037)	/* ASCII ESC */
571573Srgrimes#define	MAX_RECURSION	32		/* maximum getent recursion */
581573Srgrimes#define	SFRAG		100		/* cgetstr mallocs in SFRAG chunks */
591573Srgrimes
601573Srgrimes#define RECOK	(char)0
611573Srgrimes#define TCERR	(char)1
621573Srgrimes#define	SHADOW	(char)2
631573Srgrimes
641573Srgrimesstatic size_t	 topreclen;	/* toprec length */
651573Srgrimesstatic char	*toprec;	/* Additional record specified by cgetset() */
661573Srgrimesstatic int	 gottoprec;	/* Flag indicating retrieval of toprecord */
671573Srgrimes
68108312Salfredstatic int	cdbget(DB *, char **, const char *);
6992941Sobrienstatic int 	getent(char **, u_int *, char **, int, const char *, int, char *);
7090039Sobrienstatic int	nfcmp(char *, char *);
711573Srgrimes
721573Srgrimes/*
731573Srgrimes * Cgetset() allows the addition of a user specified buffer to be added
741573Srgrimes * to the database array, in effect "pushing" the buffer on top of the
751573Srgrimes * virtual database. 0 is returned on success, -1 on failure.
761573Srgrimes */
771573Srgrimesint
7892925Simpcgetset(const char *ent)
791573Srgrimes{
801573Srgrimes	if (ent == NULL) {
811573Srgrimes		if (toprec)
821573Srgrimes			free(toprec);
831573Srgrimes                toprec = NULL;
841573Srgrimes                topreclen = 0;
851573Srgrimes                return (0);
861573Srgrimes        }
871573Srgrimes        topreclen = strlen(ent);
881573Srgrimes        if ((toprec = malloc (topreclen + 1)) == NULL) {
891573Srgrimes		errno = ENOMEM;
901573Srgrimes                return (-1);
911573Srgrimes	}
921573Srgrimes	gottoprec = 0;
931573Srgrimes        (void)strcpy(toprec, ent);
941573Srgrimes        return (0);
951573Srgrimes}
961573Srgrimes
971573Srgrimes/*
981573Srgrimes * Cgetcap searches the capability record buf for the capability cap with
991573Srgrimes * type `type'.  A pointer to the value of cap is returned on success, NULL
1001573Srgrimes * if the requested capability couldn't be found.
1011573Srgrimes *
1021573Srgrimes * Specifying a type of ':' means that nothing should follow cap (:cap:).
1031573Srgrimes * In this case a pointer to the terminating ':' or NUL will be returned if
1041573Srgrimes * cap is found.
1051573Srgrimes *
1061573Srgrimes * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator)
1071573Srgrimes * return NULL.
1081573Srgrimes */
1091573Srgrimeschar *
11092925Simpcgetcap(char *buf, const char *cap, int type)
1111573Srgrimes{
11292941Sobrien	char *bp;
11392941Sobrien	const char *cp;
1141573Srgrimes
1151573Srgrimes	bp = buf;
1161573Srgrimes	for (;;) {
1171573Srgrimes		/*
1181573Srgrimes		 * Skip past the current capability field - it's either the
1191573Srgrimes		 * name field if this is the first time through the loop, or
1201573Srgrimes		 * the remainder of a field whose name failed to match cap.
1211573Srgrimes		 */
1221573Srgrimes		for (;;)
1231573Srgrimes			if (*bp == '\0')
1241573Srgrimes				return (NULL);
1251573Srgrimes			else
1261573Srgrimes				if (*bp++ == ':')
1271573Srgrimes					break;
1281573Srgrimes
1291573Srgrimes		/*
1301573Srgrimes		 * Try to match (cap, type) in buf.
1311573Srgrimes		 */
1321573Srgrimes		for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++)
1331573Srgrimes			continue;
1341573Srgrimes		if (*cp != '\0')
1351573Srgrimes			continue;
1361573Srgrimes		if (*bp == '@')
1371573Srgrimes			return (NULL);
1381573Srgrimes		if (type == ':') {
1391573Srgrimes			if (*bp != '\0' && *bp != ':')
1401573Srgrimes				continue;
1411573Srgrimes			return(bp);
1421573Srgrimes		}
1431573Srgrimes		if (*bp != type)
1441573Srgrimes			continue;
1451573Srgrimes		bp++;
1461573Srgrimes		return (*bp == '@' ? NULL : bp);
1471573Srgrimes	}
1481573Srgrimes	/* NOTREACHED */
1491573Srgrimes}
1501573Srgrimes
1511573Srgrimes/*
1521573Srgrimes * Cgetent extracts the capability record name from the NULL terminated file
1531573Srgrimes * array db_array and returns a pointer to a malloc'd copy of it in buf.
1541573Srgrimes * Buf must be retained through all subsequent calls to cgetcap, cgetnum,
1551573Srgrimes * cgetflag, and cgetstr, but may then be free'd.  0 is returned on success,
1561573Srgrimes * -1 if the requested record couldn't be found, -2 if a system error was
1571573Srgrimes * encountered (couldn't open/read a file, etc.), and -3 if a potential
1581573Srgrimes * reference loop is detected.
1591573Srgrimes */
1601573Srgrimesint
16192925Simpcgetent(char **buf, char **db_array, const char *name)
1621573Srgrimes{
1631573Srgrimes	u_int dummy;
1641573Srgrimes
1651573Srgrimes	return (getent(buf, &dummy, db_array, -1, name, 0, NULL));
1661573Srgrimes}
1671573Srgrimes
1681573Srgrimes/*
1691573Srgrimes * Getent implements the functions of cgetent.  If fd is non-negative,
1701573Srgrimes * *db_array has already been opened and fd is the open file descriptor.  We
1711573Srgrimes * do this to save time and avoid using up file descriptors for tc=
1721573Srgrimes * recursions.
1731573Srgrimes *
1741573Srgrimes * Getent returns the same success/failure codes as cgetent.  On success, a
1751573Srgrimes * pointer to a malloc'ed capability record with all tc= capabilities fully
1761573Srgrimes * expanded and its length (not including trailing ASCII NUL) are left in
1771573Srgrimes * *cap and *len.
1781573Srgrimes *
1791573Srgrimes * Basic algorithm:
1801573Srgrimes *	+ Allocate memory incrementally as needed in chunks of size BFRAG
1811573Srgrimes *	  for capability buffer.
1821573Srgrimes *	+ Recurse for each tc=name and interpolate result.  Stop when all
1831573Srgrimes *	  names interpolated, a name can't be found, or depth exceeds
1841573Srgrimes *	  MAX_RECURSION.
1851573Srgrimes */
1861573Srgrimesstatic int
18792941Sobriengetent(char **cap, u_int *len, char **db_array, int fd, const char *name,
18892941Sobrien    int depth, char *nfield)
1891573Srgrimes{
1901573Srgrimes	DB *capdbp;
19190039Sobrien	char *r_end, *rp, **db_p;
192190661Sdelphij	int myfd, eof, foundit, retval;
1931573Srgrimes	char *record, *cbuf;
1941573Srgrimes	int tc_not_resolved;
1951573Srgrimes	char pbuf[_POSIX_PATH_MAX];
1968870Srgrimes
1971573Srgrimes	/*
1981573Srgrimes	 * Return with ``loop detected'' error if we've recursed more than
1991573Srgrimes	 * MAX_RECURSION times.
2001573Srgrimes	 */
2011573Srgrimes	if (depth > MAX_RECURSION)
2021573Srgrimes		return (-3);
2031573Srgrimes
2041573Srgrimes	/*
2051573Srgrimes	 * Check if we have a top record from cgetset().
2061573Srgrimes         */
2071573Srgrimes	if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
2081573Srgrimes		if ((record = malloc (topreclen + BFRAG)) == NULL) {
2091573Srgrimes			errno = ENOMEM;
2101573Srgrimes			return (-2);
2111573Srgrimes		}
2121573Srgrimes		(void)strcpy(record, toprec);
2131573Srgrimes		myfd = 0;
2141573Srgrimes		db_p = db_array;
2151573Srgrimes		rp = record + topreclen + 1;
2161573Srgrimes		r_end = rp + BFRAG;
2171573Srgrimes		goto tc_exp;
2181573Srgrimes	}
2191573Srgrimes	/*
2201573Srgrimes	 * Allocate first chunk of memory.
2211573Srgrimes	 */
2221573Srgrimes	if ((record = malloc(BFRAG)) == NULL) {
2231573Srgrimes		errno = ENOMEM;
2241573Srgrimes		return (-2);
2251573Srgrimes	}
2261573Srgrimes	r_end = record + BFRAG;
2271573Srgrimes	foundit = 0;
2281573Srgrimes	/*
2291573Srgrimes	 * Loop through database array until finding the record.
2301573Srgrimes	 */
2311573Srgrimes
2321573Srgrimes	for (db_p = db_array; *db_p != NULL; db_p++) {
2331573Srgrimes		eof = 0;
2341573Srgrimes
2351573Srgrimes		/*
2361573Srgrimes		 * Open database if not already open.
2371573Srgrimes		 */
2381573Srgrimes
2391573Srgrimes		if (fd >= 0) {
24030713Sjdp			(void)lseek(fd, (off_t)0, SEEK_SET);
2411573Srgrimes			myfd = 0;
2421573Srgrimes		} else {
2431573Srgrimes			(void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p);
2441573Srgrimes			if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0))
2451573Srgrimes			     != NULL) {
2461573Srgrimes				free(record);
2471573Srgrimes				retval = cdbget(capdbp, &record, name);
2481573Srgrimes				if (retval < 0) {
2491573Srgrimes					/* no record available */
2501573Srgrimes					(void)capdbp->close(capdbp);
2511573Srgrimes					return (retval);
2521573Srgrimes				}
2531573Srgrimes				/* save the data; close frees it */
254190661Sdelphij				cbuf = strdup(record);
2551573Srgrimes				if (capdbp->close(capdbp) < 0) {
2561573Srgrimes					free(cbuf);
2571573Srgrimes					return (-2);
2581573Srgrimes				}
259190661Sdelphij				if (cbuf == NULL) {
260190661Sdelphij					errno = ENOMEM;
261190661Sdelphij					return (-2);
262190661Sdelphij				}
263192129Sdelphij				*len = strlen(cbuf);
2641573Srgrimes				*cap = cbuf;
2651573Srgrimes				return (retval);
2661573Srgrimes			} else {
267241046Sjilles				fd = _open(*db_p, O_RDONLY | O_CLOEXEC, 0);
26844921Simp				if (fd < 0)
26944921Simp					continue;
2701573Srgrimes				myfd = 1;
2711573Srgrimes			}
2721573Srgrimes		}
2731573Srgrimes		/*
2741573Srgrimes		 * Find the requested capability record ...
2751573Srgrimes		 */
2761573Srgrimes		{
2771573Srgrimes		char buf[BUFSIZ];
27890039Sobrien		char *b_end, *bp;
27990039Sobrien		int c;
2801573Srgrimes
2811573Srgrimes		/*
2821573Srgrimes		 * Loop invariants:
2831573Srgrimes		 *	There is always room for one more character in record.
2841573Srgrimes		 *	R_end always points just past end of record.
2851573Srgrimes		 *	Rp always points just past last character in record.
2861573Srgrimes		 *	B_end always points just past last character in buf.
2871573Srgrimes		 *	Bp always points at next character in buf.
2881573Srgrimes		 */
2891573Srgrimes		b_end = buf;
2901573Srgrimes		bp = buf;
2911573Srgrimes		for (;;) {
2921573Srgrimes
2931573Srgrimes			/*
2941573Srgrimes			 * Read in a line implementing (\, newline)
2951573Srgrimes			 * line continuation.
2961573Srgrimes			 */
2971573Srgrimes			rp = record;
2981573Srgrimes			for (;;) {
2991573Srgrimes				if (bp >= b_end) {
3001573Srgrimes					int n;
3018870Srgrimes
30256698Sjasone					n = _read(fd, buf, sizeof(buf));
3031573Srgrimes					if (n <= 0) {
3041573Srgrimes						if (myfd)
30556698Sjasone							(void)_close(fd);
3061573Srgrimes						if (n < 0) {
3071573Srgrimes							free(record);
3081573Srgrimes							return (-2);
3091573Srgrimes						} else {
3101573Srgrimes							fd = -1;
3111573Srgrimes							eof = 1;
3121573Srgrimes							break;
3131573Srgrimes						}
3141573Srgrimes					}
3151573Srgrimes					b_end = buf+n;
3161573Srgrimes					bp = buf;
3171573Srgrimes				}
3188870Srgrimes
3191573Srgrimes				c = *bp++;
3201573Srgrimes				if (c == '\n') {
3211573Srgrimes					if (rp > record && *(rp-1) == '\\') {
3221573Srgrimes						rp--;
3231573Srgrimes						continue;
3241573Srgrimes					} else
3251573Srgrimes						break;
3261573Srgrimes				}
3271573Srgrimes				*rp++ = c;
3281573Srgrimes
3291573Srgrimes				/*
3308870Srgrimes				 * Enforce loop invariant: if no room
3311573Srgrimes				 * left in record buffer, try to get
3321573Srgrimes				 * some more.
3331573Srgrimes				 */
3341573Srgrimes				if (rp >= r_end) {
3351573Srgrimes					u_int pos;
3361573Srgrimes					size_t newsize;
3371573Srgrimes
3381573Srgrimes					pos = rp - record;
3391573Srgrimes					newsize = r_end - record + BFRAG;
34039327Simp					record = reallocf(record, newsize);
3411573Srgrimes					if (record == NULL) {
3421573Srgrimes						errno = ENOMEM;
3431573Srgrimes						if (myfd)
34456698Sjasone							(void)_close(fd);
3451573Srgrimes						return (-2);
3461573Srgrimes					}
3471573Srgrimes					r_end = record + newsize;
3481573Srgrimes					rp = record + pos;
3491573Srgrimes				}
3501573Srgrimes			}
3511573Srgrimes				/* loop invariant let's us do this */
3521573Srgrimes			*rp++ = '\0';
3531573Srgrimes
3541573Srgrimes			/*
3551573Srgrimes			 * If encountered eof check next file.
3561573Srgrimes			 */
3571573Srgrimes			if (eof)
3581573Srgrimes				break;
3598870Srgrimes
3601573Srgrimes			/*
3611573Srgrimes			 * Toss blank lines and comments.
3621573Srgrimes			 */
3631573Srgrimes			if (*record == '\0' || *record == '#')
3641573Srgrimes				continue;
3658870Srgrimes
3661573Srgrimes			/*
3671573Srgrimes			 * See if this is the record we want ...
3681573Srgrimes			 */
3691573Srgrimes			if (cgetmatch(record, name) == 0) {
3701573Srgrimes				if (nfield == NULL || !nfcmp(nfield, record)) {
3711573Srgrimes					foundit = 1;
3721573Srgrimes					break;	/* found it! */
3731573Srgrimes				}
3741573Srgrimes			}
3751573Srgrimes		}
3761573Srgrimes	}
3771573Srgrimes		if (foundit)
3781573Srgrimes			break;
3791573Srgrimes	}
3801573Srgrimes
38160747Shoek	if (!foundit) {
38260747Shoek		free(record);
3831573Srgrimes		return (-1);
38460747Shoek	}
3851573Srgrimes
3861573Srgrimes	/*
3871573Srgrimes	 * Got the capability record, but now we have to expand all tc=name
3881573Srgrimes	 * references in it ...
3891573Srgrimes	 */
3901573Srgrimestc_exp:	{
39190039Sobrien		char *newicap, *s;
39290039Sobrien		int newilen;
3931573Srgrimes		u_int ilen;
3941573Srgrimes		int diff, iret, tclen;
3951573Srgrimes		char *icap, *scan, *tc, *tcstart, *tcend;
3961573Srgrimes
3971573Srgrimes		/*
3981573Srgrimes		 * Loop invariants:
3991573Srgrimes		 *	There is room for one more character in record.
4001573Srgrimes		 *	R_end points just past end of record.
4011573Srgrimes		 *	Rp points just past last character in record.
4021573Srgrimes		 *	Scan points at remainder of record that needs to be
4031573Srgrimes		 *	scanned for tc=name constructs.
4041573Srgrimes		 */
4051573Srgrimes		scan = record;
4061573Srgrimes		tc_not_resolved = 0;
4071573Srgrimes		for (;;) {
4081573Srgrimes			if ((tc = cgetcap(scan, "tc", '=')) == NULL)
4091573Srgrimes				break;
4101573Srgrimes
4111573Srgrimes			/*
4121573Srgrimes			 * Find end of tc=name and stomp on the trailing `:'
4131573Srgrimes			 * (if present) so we can use it to call ourselves.
4141573Srgrimes			 */
4151573Srgrimes			s = tc;
4161573Srgrimes			for (;;)
4171573Srgrimes				if (*s == '\0')
4181573Srgrimes					break;
4191573Srgrimes				else
4201573Srgrimes					if (*s++ == ':') {
4211573Srgrimes						*(s - 1) = '\0';
4221573Srgrimes						break;
4231573Srgrimes					}
4241573Srgrimes			tcstart = tc - 3;
4251573Srgrimes			tclen = s - tcstart;
4261573Srgrimes			tcend = s;
4271573Srgrimes
4288870Srgrimes			iret = getent(&icap, &ilen, db_p, fd, tc, depth+1,
4291573Srgrimes				      NULL);
4301573Srgrimes			newicap = icap;		/* Put into a register. */
4311573Srgrimes			newilen = ilen;
4321573Srgrimes			if (iret != 0) {
4331573Srgrimes				/* an error */
4341573Srgrimes				if (iret < -1) {
4351573Srgrimes					if (myfd)
43656698Sjasone						(void)_close(fd);
4371573Srgrimes					free(record);
4381573Srgrimes					return (iret);
4391573Srgrimes				}
4401573Srgrimes				if (iret == 1)
4411573Srgrimes					tc_not_resolved = 1;
4421573Srgrimes				/* couldn't resolve tc */
4431573Srgrimes				if (iret == -1) {
4448870Srgrimes					*(s - 1) = ':';
4451573Srgrimes					scan = s - 1;
4461573Srgrimes					tc_not_resolved = 1;
4471573Srgrimes					continue;
4488870Srgrimes
4491573Srgrimes				}
4501573Srgrimes			}
4511573Srgrimes			/* not interested in name field of tc'ed record */
4521573Srgrimes			s = newicap;
4531573Srgrimes			for (;;)
4541573Srgrimes				if (*s == '\0')
4551573Srgrimes					break;
4561573Srgrimes				else
4571573Srgrimes					if (*s++ == ':')
4581573Srgrimes						break;
4591573Srgrimes			newilen -= s - newicap;
4601573Srgrimes			newicap = s;
4611573Srgrimes
4621573Srgrimes			/* make sure interpolated record is `:'-terminated */
4631573Srgrimes			s += newilen;
4641573Srgrimes			if (*(s-1) != ':') {
4651573Srgrimes				*s = ':';	/* overwrite NUL with : */
4661573Srgrimes				newilen++;
4671573Srgrimes			}
4681573Srgrimes
4691573Srgrimes			/*
4701573Srgrimes			 * Make sure there's enough room to insert the
4711573Srgrimes			 * new record.
4721573Srgrimes			 */
4731573Srgrimes			diff = newilen - tclen;
4741573Srgrimes			if (diff >= r_end - rp) {
4751573Srgrimes				u_int pos, tcpos, tcposend;
4761573Srgrimes				size_t newsize;
4771573Srgrimes
4781573Srgrimes				pos = rp - record;
4791573Srgrimes				newsize = r_end - record + diff + BFRAG;
4801573Srgrimes				tcpos = tcstart - record;
4811573Srgrimes				tcposend = tcend - record;
48239327Simp				record = reallocf(record, newsize);
4831573Srgrimes				if (record == NULL) {
4841573Srgrimes					errno = ENOMEM;
4851573Srgrimes					if (myfd)
48656698Sjasone						(void)_close(fd);
4871573Srgrimes					free(icap);
4881573Srgrimes					return (-2);
4891573Srgrimes				}
4901573Srgrimes				r_end = record + newsize;
4911573Srgrimes				rp = record + pos;
4921573Srgrimes				tcstart = record + tcpos;
4931573Srgrimes				tcend = record + tcposend;
4941573Srgrimes			}
4951573Srgrimes
4961573Srgrimes			/*
4971573Srgrimes			 * Insert tc'ed record into our record.
4981573Srgrimes			 */
4991573Srgrimes			s = tcstart + newilen;
5001573Srgrimes			bcopy(tcend, s, rp - tcend);
5011573Srgrimes			bcopy(newicap, tcstart, newilen);
5021573Srgrimes			rp += diff;
5031573Srgrimes			free(icap);
5041573Srgrimes
5051573Srgrimes			/*
5061573Srgrimes			 * Start scan on `:' so next cgetcap works properly
5071573Srgrimes			 * (cgetcap always skips first field).
5081573Srgrimes			 */
5091573Srgrimes			scan = s-1;
5101573Srgrimes		}
5118870Srgrimes
5121573Srgrimes	}
5131573Srgrimes	/*
5141573Srgrimes	 * Close file (if we opened it), give back any extra memory, and
5151573Srgrimes	 * return capability, length and success.
5161573Srgrimes	 */
5171573Srgrimes	if (myfd)
51856698Sjasone		(void)_close(fd);
5191573Srgrimes	*len = rp - record - 1;	/* don't count NUL */
5201573Srgrimes	if (r_end > rp)
5218870Srgrimes		if ((record =
52239327Simp		     reallocf(record, (size_t)(rp - record))) == NULL) {
5231573Srgrimes			errno = ENOMEM;
5241573Srgrimes			return (-2);
5251573Srgrimes		}
5268870Srgrimes
5271573Srgrimes	*cap = record;
5281573Srgrimes	if (tc_not_resolved)
5291573Srgrimes		return (1);
5301573Srgrimes	return (0);
5318870Srgrimes}
5321573Srgrimes
5331573Srgrimesstatic int
534108312Salfredcdbget(DB *capdbp, char **bp, const char *name)
5351573Srgrimes{
5361573Srgrimes	DBT key, data;
537108312Salfred	char *namebuf;
5381573Srgrimes
539108312Salfred	namebuf = strdup(name);
540108312Salfred	if (namebuf == NULL)
541108312Salfred		return (-2);
542108312Salfred	key.data = namebuf;
543108312Salfred	key.size = strlen(namebuf);
5441573Srgrimes
5451573Srgrimes	for (;;) {
5461573Srgrimes		/* Get the reference. */
5471573Srgrimes		switch(capdbp->get(capdbp, &key, &data, 0)) {
5481573Srgrimes		case -1:
549108312Salfred			free(namebuf);
5501573Srgrimes			return (-2);
5511573Srgrimes		case 1:
552108312Salfred			free(namebuf);
5531573Srgrimes			return (-1);
5541573Srgrimes		}
5551573Srgrimes
5561573Srgrimes		/* If not an index to another record, leave. */
5571573Srgrimes		if (((char *)data.data)[0] != SHADOW)
5581573Srgrimes			break;
5591573Srgrimes
5601573Srgrimes		key.data = (char *)data.data + 1;
5611573Srgrimes		key.size = data.size - 1;
5621573Srgrimes	}
5638870Srgrimes
5641573Srgrimes	*bp = (char *)data.data + 1;
565108312Salfred	free(namebuf);
5661573Srgrimes	return (((char *)(data.data))[0] == TCERR ? 1 : 0);
5671573Srgrimes}
5681573Srgrimes
5691573Srgrimes/*
5701573Srgrimes * Cgetmatch will return 0 if name is one of the names of the capability
5711573Srgrimes * record buf, -1 if not.
5721573Srgrimes */
5731573Srgrimesint
57492925Simpcgetmatch(const char *buf, const char *name)
5751573Srgrimes{
576108312Salfred	const char *np, *bp;
5771573Srgrimes
578108553Sthomas	if (name == NULL || *name == '\0')
579108553Sthomas		return -1;
580108553Sthomas
5811573Srgrimes	/*
5821573Srgrimes	 * Start search at beginning of record.
5831573Srgrimes	 */
5841573Srgrimes	bp = buf;
5851573Srgrimes	for (;;) {
5861573Srgrimes		/*
5871573Srgrimes		 * Try to match a record name.
5881573Srgrimes		 */
5891573Srgrimes		np = name;
5901573Srgrimes		for (;;)
5911573Srgrimes			if (*np == '\0')
5921573Srgrimes				if (*bp == '|' || *bp == ':' || *bp == '\0')
5931573Srgrimes					return (0);
5941573Srgrimes				else
5951573Srgrimes					break;
5961573Srgrimes			else
5971573Srgrimes				if (*bp++ != *np++)
5981573Srgrimes					break;
5991573Srgrimes
6001573Srgrimes		/*
6011573Srgrimes		 * Match failed, skip to next name in record.
6021573Srgrimes		 */
6031573Srgrimes		bp--;	/* a '|' or ':' may have stopped the match */
6041573Srgrimes		for (;;)
6051573Srgrimes			if (*bp == '\0' || *bp == ':')
6061573Srgrimes				return (-1);	/* match failed totally */
6071573Srgrimes			else
6081573Srgrimes				if (*bp++ == '|')
6091573Srgrimes					break;	/* found next name */
6101573Srgrimes	}
6111573Srgrimes}
6121573Srgrimes
6131573Srgrimes
6141573Srgrimes
6151573Srgrimes
6161573Srgrimes
6171573Srgrimesint
61892925Simpcgetfirst(char **buf, char **db_array)
6191573Srgrimes{
6201573Srgrimes	(void)cgetclose();
6211573Srgrimes	return (cgetnext(buf, db_array));
6221573Srgrimes}
6231573Srgrimes
6241573Srgrimesstatic FILE *pfp;
6251573Srgrimesstatic int slash;
6261573Srgrimesstatic char **dbp;
6271573Srgrimes
6281573Srgrimesint
62992925Simpcgetclose(void)
6301573Srgrimes{
6311573Srgrimes	if (pfp != NULL) {
6321573Srgrimes		(void)fclose(pfp);
6331573Srgrimes		pfp = NULL;
6341573Srgrimes	}
6351573Srgrimes	dbp = NULL;
6361573Srgrimes	gottoprec = 0;
6371573Srgrimes	slash = 0;
6381573Srgrimes	return(0);
6391573Srgrimes}
6401573Srgrimes
6411573Srgrimes/*
6428870Srgrimes * Cgetnext() gets either the first or next entry in the logical database
6431573Srgrimes * specified by db_array.  It returns 0 upon completion of the database, 1
6441573Srgrimes * upon returning an entry with more remaining, and -1 if an error occurs.
6451573Srgrimes */
6461573Srgrimesint
64792925Simpcgetnext(char **bp, char **db_array)
6481573Srgrimes{
6491573Srgrimes	size_t len;
650199784Swollman	int done, hadreaderr, savederrno, status;
6511573Srgrimes	char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
6521573Srgrimes	u_int dummy;
6531573Srgrimes
6541573Srgrimes	if (dbp == NULL)
6551573Srgrimes		dbp = db_array;
6561573Srgrimes
657244092Sjilles	if (pfp == NULL && (pfp = fopen(*dbp, "re")) == NULL) {
6581573Srgrimes		(void)cgetclose();
6591573Srgrimes		return (-1);
6601573Srgrimes	}
661199784Swollman	for (;;) {
6621573Srgrimes		if (toprec && !gottoprec) {
6631573Srgrimes			gottoprec = 1;
6641573Srgrimes			line = toprec;
6651573Srgrimes		} else {
6661573Srgrimes			line = fgetln(pfp, &len);
6671573Srgrimes			if (line == NULL && pfp) {
66869502Sgad				hadreaderr = ferror(pfp);
66969502Sgad				if (hadreaderr)
67069502Sgad					savederrno = errno;
67169502Sgad				fclose(pfp);
67269502Sgad				pfp = NULL;
67369502Sgad				if (hadreaderr) {
67469502Sgad					cgetclose();
67569502Sgad					errno = savederrno;
6761573Srgrimes					return (-1);
6771573Srgrimes				} else {
6781573Srgrimes					if (*++dbp == NULL) {
6791573Srgrimes						(void)cgetclose();
6801573Srgrimes						return (0);
6811573Srgrimes					} else if ((pfp =
682244092Sjilles					    fopen(*dbp, "re")) == NULL) {
6831573Srgrimes						(void)cgetclose();
6841573Srgrimes						return (-1);
6851573Srgrimes					} else
6861573Srgrimes						continue;
6871573Srgrimes				}
6881573Srgrimes			} else
6891573Srgrimes				line[len - 1] = '\0';
6901573Srgrimes			if (len == 1) {
6911573Srgrimes				slash = 0;
6921573Srgrimes				continue;
6931573Srgrimes			}
69452856Sache			if (isspace((unsigned char)*line) ||
6951573Srgrimes			    *line == ':' || *line == '#' || slash) {
6961573Srgrimes				if (line[len - 2] == '\\')
6971573Srgrimes					slash = 1;
6981573Srgrimes				else
6991573Srgrimes					slash = 0;
7001573Srgrimes				continue;
7011573Srgrimes			}
7021573Srgrimes			if (line[len - 2] == '\\')
7031573Srgrimes				slash = 1;
7041573Srgrimes			else
7051573Srgrimes				slash = 0;
7068870Srgrimes		}
7071573Srgrimes
7081573Srgrimes
7098870Srgrimes		/*
7101573Srgrimes		 * Line points to a name line.
7111573Srgrimes		 */
7121573Srgrimes		done = 0;
7131573Srgrimes		np = nbuf;
7141573Srgrimes		for (;;) {
7151573Srgrimes			for (cp = line; *cp != '\0'; cp++) {
7161573Srgrimes				if (*cp == ':') {
7171573Srgrimes					*np++ = ':';
7181573Srgrimes					done = 1;
7191573Srgrimes					break;
7201573Srgrimes				}
7211573Srgrimes				if (*cp == '\\')
7221573Srgrimes					break;
7231573Srgrimes				*np++ = *cp;
7241573Srgrimes			}
7251573Srgrimes			if (done) {
7261573Srgrimes				*np = '\0';
7271573Srgrimes				break;
7281573Srgrimes			} else { /* name field extends beyond the line */
7291573Srgrimes				line = fgetln(pfp, &len);
7301573Srgrimes				if (line == NULL && pfp) {
73169502Sgad					/* Name extends beyond the EOF! */
73269502Sgad					hadreaderr = ferror(pfp);
73369502Sgad					if (hadreaderr)
73469502Sgad						savederrno = errno;
73569502Sgad					fclose(pfp);
73669502Sgad					pfp = NULL;
73769502Sgad					if (hadreaderr) {
73869502Sgad						cgetclose();
73969502Sgad						errno = savederrno;
7401573Srgrimes						return (-1);
74169502Sgad					} else {
74269502Sgad						cgetclose();
74369502Sgad						return (-1);
7441573Srgrimes					}
7451573Srgrimes				} else
7461573Srgrimes					line[len - 1] = '\0';
7471573Srgrimes			}
7481573Srgrimes		}
7491573Srgrimes		rp = buf;
75029574Sphk		for(cp = nbuf; *cp != '\0'; cp++)
7511573Srgrimes			if (*cp == '|' || *cp == ':')
7521573Srgrimes				break;
7531573Srgrimes			else
7541573Srgrimes				*rp++ = *cp;
7551573Srgrimes
7561573Srgrimes		*rp = '\0';
7578870Srgrimes		/*
7588870Srgrimes		 * XXX
7591573Srgrimes		 * Last argument of getent here should be nbuf if we want true
7608870Srgrimes		 * sequential access in the case of duplicates.
7611573Srgrimes		 * With NULL, getent will return the first entry found
7628870Srgrimes		 * rather than the duplicate entry record.  This is a
7631573Srgrimes		 * matter of semantics that should be resolved.
7641573Srgrimes		 */
7651573Srgrimes		status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
7661573Srgrimes		if (status == -2 || status == -3)
7671573Srgrimes			(void)cgetclose();
7681573Srgrimes
7691573Srgrimes		return (status + 1);
7701573Srgrimes	}
7711573Srgrimes	/* NOTREACHED */
7721573Srgrimes}
7731573Srgrimes
7741573Srgrimes/*
7751573Srgrimes * Cgetstr retrieves the value of the string capability cap from the
7761573Srgrimes * capability record pointed to by buf.  A pointer to a decoded, NUL
7771573Srgrimes * terminated, malloc'd copy of the string is returned in the char *
7781573Srgrimes * pointed to by str.  The length of the string not including the trailing
7791573Srgrimes * NUL is returned on success, -1 if the requested string capability
7801573Srgrimes * couldn't be found, -2 if a system error was encountered (storage
7811573Srgrimes * allocation failure).
7821573Srgrimes */
7831573Srgrimesint
78492925Simpcgetstr(char *buf, const char *cap, char **str)
7851573Srgrimes{
78690039Sobrien	u_int m_room;
78790039Sobrien	char *bp, *mp;
7881573Srgrimes	int len;
7891573Srgrimes	char *mem;
7901573Srgrimes
7911573Srgrimes	/*
7921573Srgrimes	 * Find string capability cap
7931573Srgrimes	 */
7941573Srgrimes	bp = cgetcap(buf, cap, '=');
7951573Srgrimes	if (bp == NULL)
7961573Srgrimes		return (-1);
7971573Srgrimes
7981573Srgrimes	/*
7991573Srgrimes	 * Conversion / storage allocation loop ...  Allocate memory in
8001573Srgrimes	 * chunks SFRAG in size.
8011573Srgrimes	 */
8021573Srgrimes	if ((mem = malloc(SFRAG)) == NULL) {
8031573Srgrimes		errno = ENOMEM;
8041573Srgrimes		return (-2);	/* couldn't even allocate the first fragment */
8051573Srgrimes	}
8061573Srgrimes	m_room = SFRAG;
8071573Srgrimes	mp = mem;
8081573Srgrimes
8091573Srgrimes	while (*bp != ':' && *bp != '\0') {
8101573Srgrimes		/*
8111573Srgrimes		 * Loop invariants:
8121573Srgrimes		 *	There is always room for one more character in mem.
8131573Srgrimes		 *	Mp always points just past last character in mem.
8141573Srgrimes		 *	Bp always points at next character in buf.
8151573Srgrimes		 */
8161573Srgrimes		if (*bp == '^') {
8171573Srgrimes			bp++;
8181573Srgrimes			if (*bp == ':' || *bp == '\0')
8191573Srgrimes				break;	/* drop unfinished escape */
8208522Sache			if (*bp == '?') {
8218522Sache				*mp++ = '\177';
8228522Sache				bp++;
8238522Sache			} else
8248522Sache				*mp++ = *bp++ & 037;
8251573Srgrimes		} else if (*bp == '\\') {
8261573Srgrimes			bp++;
8271573Srgrimes			if (*bp == ':' || *bp == '\0')
8281573Srgrimes				break;	/* drop unfinished escape */
8291573Srgrimes			if ('0' <= *bp && *bp <= '7') {
83090039Sobrien				int n, i;
8311573Srgrimes
8321573Srgrimes				n = 0;
8331573Srgrimes				i = 3;	/* maximum of three octal digits */
8341573Srgrimes				do {
8351573Srgrimes					n = n * 8 + (*bp++ - '0');
8361573Srgrimes				} while (--i && '0' <= *bp && *bp <= '7');
8371573Srgrimes				*mp++ = n;
8381573Srgrimes			}
8391573Srgrimes			else switch (*bp++) {
8401573Srgrimes				case 'b': case 'B':
8411573Srgrimes					*mp++ = '\b';
8421573Srgrimes					break;
8431573Srgrimes				case 't': case 'T':
8441573Srgrimes					*mp++ = '\t';
8451573Srgrimes					break;
8461573Srgrimes				case 'n': case 'N':
8471573Srgrimes					*mp++ = '\n';
8481573Srgrimes					break;
8491573Srgrimes				case 'f': case 'F':
8501573Srgrimes					*mp++ = '\f';
8511573Srgrimes					break;
8521573Srgrimes				case 'r': case 'R':
8531573Srgrimes					*mp++ = '\r';
8541573Srgrimes					break;
8551573Srgrimes				case 'e': case 'E':
8561573Srgrimes					*mp++ = ESC;
8571573Srgrimes					break;
8581573Srgrimes				case 'c': case 'C':
8591573Srgrimes					*mp++ = ':';
8601573Srgrimes					break;
8611573Srgrimes				default:
8621573Srgrimes					/*
8631573Srgrimes					 * Catches '\', '^', and
8641573Srgrimes					 *  everything else.
8651573Srgrimes					 */
8661573Srgrimes					*mp++ = *(bp-1);
8671573Srgrimes					break;
8681573Srgrimes			}
8691573Srgrimes		} else
8701573Srgrimes			*mp++ = *bp++;
8711573Srgrimes		m_room--;
8721573Srgrimes
8731573Srgrimes		/*
8741573Srgrimes		 * Enforce loop invariant: if no room left in current
8751573Srgrimes		 * buffer, try to get some more.
8761573Srgrimes		 */
8771573Srgrimes		if (m_room == 0) {
8781573Srgrimes			size_t size = mp - mem;
8791573Srgrimes
88039327Simp			if ((mem = reallocf(mem, size + SFRAG)) == NULL)
8811573Srgrimes				return (-2);
8821573Srgrimes			m_room = SFRAG;
8831573Srgrimes			mp = mem + size;
8841573Srgrimes		}
8851573Srgrimes	}
8861573Srgrimes	*mp++ = '\0';	/* loop invariant let's us do this */
8871573Srgrimes	m_room--;
8881573Srgrimes	len = mp - mem - 1;
8891573Srgrimes
8901573Srgrimes	/*
8911573Srgrimes	 * Give back any extra memory and return value and success.
8921573Srgrimes	 */
8931573Srgrimes	if (m_room != 0)
89439327Simp		if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
8951573Srgrimes			return (-2);
8961573Srgrimes	*str = mem;
8971573Srgrimes	return (len);
8981573Srgrimes}
8991573Srgrimes
9001573Srgrimes/*
9011573Srgrimes * Cgetustr retrieves the value of the string capability cap from the
9021573Srgrimes * capability record pointed to by buf.  The difference between cgetustr()
9031573Srgrimes * and cgetstr() is that cgetustr does not decode escapes but rather treats
9048870Srgrimes * all characters literally.  A pointer to a  NUL terminated malloc'd
9058870Srgrimes * copy of the string is returned in the char pointed to by str.  The
9061573Srgrimes * length of the string not including the trailing NUL is returned on success,
9078870Srgrimes * -1 if the requested string capability couldn't be found, -2 if a system
9081573Srgrimes * error was encountered (storage allocation failure).
9091573Srgrimes */
9101573Srgrimesint
91192925Simpcgetustr(char *buf, const char *cap, char **str)
9121573Srgrimes{
91390039Sobrien	u_int m_room;
91490039Sobrien	char *bp, *mp;
9151573Srgrimes	int len;
9161573Srgrimes	char *mem;
9171573Srgrimes
9181573Srgrimes	/*
9191573Srgrimes	 * Find string capability cap
9201573Srgrimes	 */
9211573Srgrimes	if ((bp = cgetcap(buf, cap, '=')) == NULL)
9221573Srgrimes		return (-1);
9231573Srgrimes
9241573Srgrimes	/*
9251573Srgrimes	 * Conversion / storage allocation loop ...  Allocate memory in
9261573Srgrimes	 * chunks SFRAG in size.
9271573Srgrimes	 */
9281573Srgrimes	if ((mem = malloc(SFRAG)) == NULL) {
9291573Srgrimes		errno = ENOMEM;
9301573Srgrimes		return (-2);	/* couldn't even allocate the first fragment */
9311573Srgrimes	}
9321573Srgrimes	m_room = SFRAG;
9331573Srgrimes	mp = mem;
9341573Srgrimes
9351573Srgrimes	while (*bp != ':' && *bp != '\0') {
9361573Srgrimes		/*
9371573Srgrimes		 * Loop invariants:
9381573Srgrimes		 *	There is always room for one more character in mem.
9391573Srgrimes		 *	Mp always points just past last character in mem.
9401573Srgrimes		 *	Bp always points at next character in buf.
9411573Srgrimes		 */
9421573Srgrimes		*mp++ = *bp++;
9431573Srgrimes		m_room--;
9441573Srgrimes
9451573Srgrimes		/*
9461573Srgrimes		 * Enforce loop invariant: if no room left in current
9471573Srgrimes		 * buffer, try to get some more.
9481573Srgrimes		 */
9491573Srgrimes		if (m_room == 0) {
9501573Srgrimes			size_t size = mp - mem;
9511573Srgrimes
95239327Simp			if ((mem = reallocf(mem, size + SFRAG)) == NULL)
9531573Srgrimes				return (-2);
9541573Srgrimes			m_room = SFRAG;
9551573Srgrimes			mp = mem + size;
9561573Srgrimes		}
9571573Srgrimes	}
9581573Srgrimes	*mp++ = '\0';	/* loop invariant let's us do this */
9591573Srgrimes	m_room--;
9601573Srgrimes	len = mp - mem - 1;
9611573Srgrimes
9621573Srgrimes	/*
9631573Srgrimes	 * Give back any extra memory and return value and success.
9641573Srgrimes	 */
9651573Srgrimes	if (m_room != 0)
96639327Simp		if ((mem = reallocf(mem, (size_t)(mp - mem))) == NULL)
9671573Srgrimes			return (-2);
9681573Srgrimes	*str = mem;
9691573Srgrimes	return (len);
9701573Srgrimes}
9711573Srgrimes
9721573Srgrimes/*
9731573Srgrimes * Cgetnum retrieves the value of the numeric capability cap from the
9741573Srgrimes * capability record pointed to by buf.  The numeric value is returned in
9751573Srgrimes * the long pointed to by num.  0 is returned on success, -1 if the requested
9761573Srgrimes * numeric capability couldn't be found.
9771573Srgrimes */
9781573Srgrimesint
97992925Simpcgetnum(char *buf, const char *cap, long *num)
9801573Srgrimes{
98190039Sobrien	long n;
98290039Sobrien	int base, digit;
98390039Sobrien	char *bp;
9841573Srgrimes
9851573Srgrimes	/*
9861573Srgrimes	 * Find numeric capability cap
9871573Srgrimes	 */
9881573Srgrimes	bp = cgetcap(buf, cap, '#');
9891573Srgrimes	if (bp == NULL)
9901573Srgrimes		return (-1);
9911573Srgrimes
9921573Srgrimes	/*
9931573Srgrimes	 * Look at value and determine numeric base:
9941573Srgrimes	 *	0x... or 0X...	hexadecimal,
9951573Srgrimes	 * else	0...		octal,
9961573Srgrimes	 * else			decimal.
9971573Srgrimes	 */
9981573Srgrimes	if (*bp == '0') {
9991573Srgrimes		bp++;
10001573Srgrimes		if (*bp == 'x' || *bp == 'X') {
10011573Srgrimes			bp++;
10021573Srgrimes			base = 16;
10031573Srgrimes		} else
10041573Srgrimes			base = 8;
10051573Srgrimes	} else
10061573Srgrimes		base = 10;
10071573Srgrimes
10081573Srgrimes	/*
10091573Srgrimes	 * Conversion loop ...
10101573Srgrimes	 */
10111573Srgrimes	n = 0;
10121573Srgrimes	for (;;) {
10131573Srgrimes		if ('0' <= *bp && *bp <= '9')
10141573Srgrimes			digit = *bp - '0';
10151573Srgrimes		else if ('a' <= *bp && *bp <= 'f')
10161573Srgrimes			digit = 10 + *bp - 'a';
10171573Srgrimes		else if ('A' <= *bp && *bp <= 'F')
10181573Srgrimes			digit = 10 + *bp - 'A';
10191573Srgrimes		else
10201573Srgrimes			break;
10211573Srgrimes
10221573Srgrimes		if (digit >= base)
10231573Srgrimes			break;
10241573Srgrimes
10251573Srgrimes		n = n * base + digit;
10261573Srgrimes		bp++;
10271573Srgrimes	}
10281573Srgrimes
10291573Srgrimes	/*
10301573Srgrimes	 * Return value and success.
10311573Srgrimes	 */
10321573Srgrimes	*num = n;
10331573Srgrimes	return (0);
10341573Srgrimes}
10351573Srgrimes
10361573Srgrimes
10371573Srgrimes/*
10381573Srgrimes * Compare name field of record.
10391573Srgrimes */
10401573Srgrimesstatic int
104192925Simpnfcmp(char *nf, char *rec)
10421573Srgrimes{
10431573Srgrimes	char *cp, tmp;
10441573Srgrimes	int ret;
10458870Srgrimes
10461573Srgrimes	for (cp = rec; *cp != ':'; cp++)
10471573Srgrimes		;
10488870Srgrimes
10491573Srgrimes	tmp = *(cp + 1);
10501573Srgrimes	*(cp + 1) = '\0';
10511573Srgrimes	ret = strcmp(nf, rec);
10521573Srgrimes	*(cp + 1) = tmp;
10531573Srgrimes
10541573Srgrimes	return (ret);
10551573Srgrimes}
1056