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