getcap.c revision 108312
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 * 3. All advertising materials mentioning features or use of this software 171573Srgrimes * must display the following acknowledgement: 181573Srgrimes * This product includes software developed by the University of 191573Srgrimes * California, Berkeley and its contributors. 201573Srgrimes * 4. Neither the name of the University nor the names of its contributors 211573Srgrimes * may be used to endorse or promote products derived from this software 221573Srgrimes * without specific prior written permission. 231573Srgrimes * 241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341573Srgrimes * SUCH DAMAGE. 351573Srgrimes */ 361573Srgrimes 371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 381573Srgrimesstatic char sccsid[] = "@(#)getcap.c 8.3 (Berkeley) 3/25/94"; 391573Srgrimes#endif /* LIBC_SCCS and not lint */ 4090039Sobrien#include <sys/cdefs.h> 4190039Sobrien__FBSDID("$FreeBSD: head/lib/libc/gen/getcap.c 108312 2002-12-27 08:43:40Z alfred $"); 421573Srgrimes 4371579Sdeischen#include "namespace.h" 441573Srgrimes#include <sys/types.h> 451573Srgrimes 461573Srgrimes#include <ctype.h> 478870Srgrimes#include <errno.h> 481573Srgrimes#include <fcntl.h> 491573Srgrimes#include <limits.h> 501573Srgrimes#include <stdio.h> 511573Srgrimes#include <stdlib.h> 521573Srgrimes#include <string.h> 531573Srgrimes#include <unistd.h> 5471579Sdeischen#include "un-namespace.h" 551573Srgrimes 5671579Sdeischen#include <db.h> 5771579Sdeischen 581573Srgrimes#define BFRAG 1024 591573Srgrimes#define BSIZE 1024 601573Srgrimes#define ESC ('[' & 037) /* ASCII ESC */ 611573Srgrimes#define MAX_RECURSION 32 /* maximum getent recursion */ 621573Srgrimes#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */ 631573Srgrimes 641573Srgrimes#define RECOK (char)0 651573Srgrimes#define TCERR (char)1 661573Srgrimes#define SHADOW (char)2 671573Srgrimes 681573Srgrimesstatic size_t topreclen; /* toprec length */ 691573Srgrimesstatic char *toprec; /* Additional record specified by cgetset() */ 701573Srgrimesstatic int gottoprec; /* Flag indicating retrieval of toprecord */ 711573Srgrimes 72108312Salfredstatic int cdbget(DB *, char **, const char *); 7392941Sobrienstatic int getent(char **, u_int *, char **, int, const char *, int, char *); 7490039Sobrienstatic int nfcmp(char *, char *); 751573Srgrimes 761573Srgrimes/* 771573Srgrimes * Cgetset() allows the addition of a user specified buffer to be added 781573Srgrimes * to the database array, in effect "pushing" the buffer on top of the 791573Srgrimes * virtual database. 0 is returned on success, -1 on failure. 801573Srgrimes */ 811573Srgrimesint 8292925Simpcgetset(const char *ent) 831573Srgrimes{ 841573Srgrimes if (ent == NULL) { 851573Srgrimes if (toprec) 861573Srgrimes free(toprec); 871573Srgrimes toprec = NULL; 881573Srgrimes topreclen = 0; 891573Srgrimes return (0); 901573Srgrimes } 911573Srgrimes topreclen = strlen(ent); 921573Srgrimes if ((toprec = malloc (topreclen + 1)) == NULL) { 931573Srgrimes errno = ENOMEM; 941573Srgrimes return (-1); 951573Srgrimes } 961573Srgrimes gottoprec = 0; 971573Srgrimes (void)strcpy(toprec, ent); 981573Srgrimes return (0); 991573Srgrimes} 1001573Srgrimes 1011573Srgrimes/* 1021573Srgrimes * Cgetcap searches the capability record buf for the capability cap with 1031573Srgrimes * type `type'. A pointer to the value of cap is returned on success, NULL 1041573Srgrimes * if the requested capability couldn't be found. 1051573Srgrimes * 1061573Srgrimes * Specifying a type of ':' means that nothing should follow cap (:cap:). 1071573Srgrimes * In this case a pointer to the terminating ':' or NUL will be returned if 1081573Srgrimes * cap is found. 1091573Srgrimes * 1101573Srgrimes * If (cap, '@') or (cap, terminator, '@') is found before (cap, terminator) 1111573Srgrimes * return NULL. 1121573Srgrimes */ 1131573Srgrimeschar * 11492925Simpcgetcap(char *buf, const char *cap, int type) 1151573Srgrimes{ 11692941Sobrien char *bp; 11792941Sobrien const char *cp; 1181573Srgrimes 1191573Srgrimes bp = buf; 1201573Srgrimes for (;;) { 1211573Srgrimes /* 1221573Srgrimes * Skip past the current capability field - it's either the 1231573Srgrimes * name field if this is the first time through the loop, or 1241573Srgrimes * the remainder of a field whose name failed to match cap. 1251573Srgrimes */ 1261573Srgrimes for (;;) 1271573Srgrimes if (*bp == '\0') 1281573Srgrimes return (NULL); 1291573Srgrimes else 1301573Srgrimes if (*bp++ == ':') 1311573Srgrimes break; 1321573Srgrimes 1331573Srgrimes /* 1341573Srgrimes * Try to match (cap, type) in buf. 1351573Srgrimes */ 1361573Srgrimes for (cp = cap; *cp == *bp && *bp != '\0'; cp++, bp++) 1371573Srgrimes continue; 1381573Srgrimes if (*cp != '\0') 1391573Srgrimes continue; 1401573Srgrimes if (*bp == '@') 1411573Srgrimes return (NULL); 1421573Srgrimes if (type == ':') { 1431573Srgrimes if (*bp != '\0' && *bp != ':') 1441573Srgrimes continue; 1451573Srgrimes return(bp); 1461573Srgrimes } 1471573Srgrimes if (*bp != type) 1481573Srgrimes continue; 1491573Srgrimes bp++; 1501573Srgrimes return (*bp == '@' ? NULL : bp); 1511573Srgrimes } 1521573Srgrimes /* NOTREACHED */ 1531573Srgrimes} 1541573Srgrimes 1551573Srgrimes/* 1561573Srgrimes * Cgetent extracts the capability record name from the NULL terminated file 1571573Srgrimes * array db_array and returns a pointer to a malloc'd copy of it in buf. 1581573Srgrimes * Buf must be retained through all subsequent calls to cgetcap, cgetnum, 1591573Srgrimes * cgetflag, and cgetstr, but may then be free'd. 0 is returned on success, 1601573Srgrimes * -1 if the requested record couldn't be found, -2 if a system error was 1611573Srgrimes * encountered (couldn't open/read a file, etc.), and -3 if a potential 1621573Srgrimes * reference loop is detected. 1631573Srgrimes */ 1641573Srgrimesint 16592925Simpcgetent(char **buf, char **db_array, const char *name) 1661573Srgrimes{ 1671573Srgrimes u_int dummy; 1681573Srgrimes 1691573Srgrimes return (getent(buf, &dummy, db_array, -1, name, 0, NULL)); 1701573Srgrimes} 1711573Srgrimes 1721573Srgrimes/* 1731573Srgrimes * Getent implements the functions of cgetent. If fd is non-negative, 1741573Srgrimes * *db_array has already been opened and fd is the open file descriptor. We 1751573Srgrimes * do this to save time and avoid using up file descriptors for tc= 1761573Srgrimes * recursions. 1771573Srgrimes * 1781573Srgrimes * Getent returns the same success/failure codes as cgetent. On success, a 1791573Srgrimes * pointer to a malloc'ed capability record with all tc= capabilities fully 1801573Srgrimes * expanded and its length (not including trailing ASCII NUL) are left in 1811573Srgrimes * *cap and *len. 1821573Srgrimes * 1831573Srgrimes * Basic algorithm: 1841573Srgrimes * + Allocate memory incrementally as needed in chunks of size BFRAG 1851573Srgrimes * for capability buffer. 1861573Srgrimes * + Recurse for each tc=name and interpolate result. Stop when all 1871573Srgrimes * names interpolated, a name can't be found, or depth exceeds 1881573Srgrimes * MAX_RECURSION. 1891573Srgrimes */ 1901573Srgrimesstatic int 19192941Sobriengetent(char **cap, u_int *len, char **db_array, int fd, const char *name, 19292941Sobrien int depth, char *nfield) 1931573Srgrimes{ 1941573Srgrimes DB *capdbp; 19590039Sobrien char *r_end, *rp, **db_p; 1961573Srgrimes int myfd, eof, foundit, retval, clen; 1971573Srgrimes char *record, *cbuf; 1981573Srgrimes int tc_not_resolved; 1991573Srgrimes char pbuf[_POSIX_PATH_MAX]; 2008870Srgrimes 2011573Srgrimes /* 2021573Srgrimes * Return with ``loop detected'' error if we've recursed more than 2031573Srgrimes * MAX_RECURSION times. 2041573Srgrimes */ 2051573Srgrimes if (depth > MAX_RECURSION) 2061573Srgrimes return (-3); 2071573Srgrimes 2081573Srgrimes /* 2091573Srgrimes * Check if we have a top record from cgetset(). 2101573Srgrimes */ 2111573Srgrimes if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) { 2121573Srgrimes if ((record = malloc (topreclen + BFRAG)) == NULL) { 2131573Srgrimes errno = ENOMEM; 2141573Srgrimes return (-2); 2151573Srgrimes } 2161573Srgrimes (void)strcpy(record, toprec); 2171573Srgrimes myfd = 0; 2181573Srgrimes db_p = db_array; 2191573Srgrimes rp = record + topreclen + 1; 2201573Srgrimes r_end = rp + BFRAG; 2211573Srgrimes goto tc_exp; 2221573Srgrimes } 2231573Srgrimes /* 2241573Srgrimes * Allocate first chunk of memory. 2251573Srgrimes */ 2261573Srgrimes if ((record = malloc(BFRAG)) == NULL) { 2271573Srgrimes errno = ENOMEM; 2281573Srgrimes return (-2); 2291573Srgrimes } 2301573Srgrimes r_end = record + BFRAG; 2311573Srgrimes foundit = 0; 2321573Srgrimes /* 2331573Srgrimes * Loop through database array until finding the record. 2341573Srgrimes */ 2351573Srgrimes 2361573Srgrimes for (db_p = db_array; *db_p != NULL; db_p++) { 2371573Srgrimes eof = 0; 2381573Srgrimes 2391573Srgrimes /* 2401573Srgrimes * Open database if not already open. 2411573Srgrimes */ 2421573Srgrimes 2431573Srgrimes if (fd >= 0) { 24430713Sjdp (void)lseek(fd, (off_t)0, SEEK_SET); 2451573Srgrimes myfd = 0; 2461573Srgrimes } else { 2471573Srgrimes (void)snprintf(pbuf, sizeof(pbuf), "%s.db", *db_p); 2481573Srgrimes if ((capdbp = dbopen(pbuf, O_RDONLY, 0, DB_HASH, 0)) 2491573Srgrimes != NULL) { 2501573Srgrimes free(record); 2511573Srgrimes retval = cdbget(capdbp, &record, name); 2521573Srgrimes if (retval < 0) { 2531573Srgrimes /* no record available */ 2541573Srgrimes (void)capdbp->close(capdbp); 2551573Srgrimes return (retval); 2561573Srgrimes } 2571573Srgrimes /* save the data; close frees it */ 2581573Srgrimes clen = strlen(record); 2591573Srgrimes cbuf = malloc(clen + 1); 2601573Srgrimes memcpy(cbuf, record, clen + 1); 2611573Srgrimes if (capdbp->close(capdbp) < 0) { 2621573Srgrimes free(cbuf); 2631573Srgrimes return (-2); 2641573Srgrimes } 2651573Srgrimes *len = clen; 2661573Srgrimes *cap = cbuf; 2671573Srgrimes return (retval); 2681573Srgrimes } else { 26956698Sjasone fd = _open(*db_p, O_RDONLY, 0); 27044921Simp if (fd < 0) 27144921Simp continue; 2721573Srgrimes myfd = 1; 2731573Srgrimes } 2741573Srgrimes } 2751573Srgrimes /* 2761573Srgrimes * Find the requested capability record ... 2771573Srgrimes */ 2781573Srgrimes { 2791573Srgrimes char buf[BUFSIZ]; 28090039Sobrien char *b_end, *bp; 28190039Sobrien int c; 2821573Srgrimes 2831573Srgrimes /* 2841573Srgrimes * Loop invariants: 2851573Srgrimes * There is always room for one more character in record. 2861573Srgrimes * R_end always points just past end of record. 2871573Srgrimes * Rp always points just past last character in record. 2881573Srgrimes * B_end always points just past last character in buf. 2891573Srgrimes * Bp always points at next character in buf. 2901573Srgrimes */ 2911573Srgrimes b_end = buf; 2921573Srgrimes bp = buf; 2931573Srgrimes for (;;) { 2941573Srgrimes 2951573Srgrimes /* 2961573Srgrimes * Read in a line implementing (\, newline) 2971573Srgrimes * line continuation. 2981573Srgrimes */ 2991573Srgrimes rp = record; 3001573Srgrimes for (;;) { 3011573Srgrimes if (bp >= b_end) { 3021573Srgrimes int n; 3038870Srgrimes 30456698Sjasone n = _read(fd, buf, sizeof(buf)); 3051573Srgrimes if (n <= 0) { 3061573Srgrimes if (myfd) 30756698Sjasone (void)_close(fd); 3081573Srgrimes if (n < 0) { 3091573Srgrimes free(record); 3101573Srgrimes return (-2); 3111573Srgrimes } else { 3121573Srgrimes fd = -1; 3131573Srgrimes eof = 1; 3141573Srgrimes break; 3151573Srgrimes } 3161573Srgrimes } 3171573Srgrimes b_end = buf+n; 3181573Srgrimes bp = buf; 3191573Srgrimes } 3208870Srgrimes 3211573Srgrimes c = *bp++; 3221573Srgrimes if (c == '\n') { 3231573Srgrimes if (rp > record && *(rp-1) == '\\') { 3241573Srgrimes rp--; 3251573Srgrimes continue; 3261573Srgrimes } else 3271573Srgrimes break; 3281573Srgrimes } 3291573Srgrimes *rp++ = c; 3301573Srgrimes 3311573Srgrimes /* 3328870Srgrimes * Enforce loop invariant: if no room 3331573Srgrimes * left in record buffer, try to get 3341573Srgrimes * some more. 3351573Srgrimes */ 3361573Srgrimes if (rp >= r_end) { 3371573Srgrimes u_int pos; 3381573Srgrimes size_t newsize; 3391573Srgrimes 3401573Srgrimes pos = rp - record; 3411573Srgrimes newsize = r_end - record + BFRAG; 34239327Simp record = reallocf(record, newsize); 3431573Srgrimes if (record == NULL) { 3441573Srgrimes errno = ENOMEM; 3451573Srgrimes if (myfd) 34656698Sjasone (void)_close(fd); 3471573Srgrimes return (-2); 3481573Srgrimes } 3491573Srgrimes r_end = record + newsize; 3501573Srgrimes rp = record + pos; 3511573Srgrimes } 3521573Srgrimes } 3531573Srgrimes /* loop invariant let's us do this */ 3541573Srgrimes *rp++ = '\0'; 3551573Srgrimes 3561573Srgrimes /* 3571573Srgrimes * If encountered eof check next file. 3581573Srgrimes */ 3591573Srgrimes if (eof) 3601573Srgrimes break; 3618870Srgrimes 3621573Srgrimes /* 3631573Srgrimes * Toss blank lines and comments. 3641573Srgrimes */ 3651573Srgrimes if (*record == '\0' || *record == '#') 3661573Srgrimes continue; 3678870Srgrimes 3681573Srgrimes /* 3691573Srgrimes * See if this is the record we want ... 3701573Srgrimes */ 3711573Srgrimes if (cgetmatch(record, name) == 0) { 3721573Srgrimes if (nfield == NULL || !nfcmp(nfield, record)) { 3731573Srgrimes foundit = 1; 3741573Srgrimes break; /* found it! */ 3751573Srgrimes } 3761573Srgrimes } 3771573Srgrimes } 3781573Srgrimes } 3791573Srgrimes if (foundit) 3801573Srgrimes break; 3811573Srgrimes } 3821573Srgrimes 38360747Shoek if (!foundit) { 38460747Shoek free(record); 3851573Srgrimes return (-1); 38660747Shoek } 3871573Srgrimes 3881573Srgrimes /* 3891573Srgrimes * Got the capability record, but now we have to expand all tc=name 3901573Srgrimes * references in it ... 3911573Srgrimes */ 3921573Srgrimestc_exp: { 39390039Sobrien char *newicap, *s; 39490039Sobrien int newilen; 3951573Srgrimes u_int ilen; 3961573Srgrimes int diff, iret, tclen; 3971573Srgrimes char *icap, *scan, *tc, *tcstart, *tcend; 3981573Srgrimes 3991573Srgrimes /* 4001573Srgrimes * Loop invariants: 4011573Srgrimes * There is room for one more character in record. 4021573Srgrimes * R_end points just past end of record. 4031573Srgrimes * Rp points just past last character in record. 4041573Srgrimes * Scan points at remainder of record that needs to be 4051573Srgrimes * scanned for tc=name constructs. 4061573Srgrimes */ 4071573Srgrimes scan = record; 4081573Srgrimes tc_not_resolved = 0; 4091573Srgrimes for (;;) { 4101573Srgrimes if ((tc = cgetcap(scan, "tc", '=')) == NULL) 4111573Srgrimes break; 4121573Srgrimes 4131573Srgrimes /* 4141573Srgrimes * Find end of tc=name and stomp on the trailing `:' 4151573Srgrimes * (if present) so we can use it to call ourselves. 4161573Srgrimes */ 4171573Srgrimes s = tc; 4181573Srgrimes for (;;) 4191573Srgrimes if (*s == '\0') 4201573Srgrimes break; 4211573Srgrimes else 4221573Srgrimes if (*s++ == ':') { 4231573Srgrimes *(s - 1) = '\0'; 4241573Srgrimes break; 4251573Srgrimes } 4261573Srgrimes tcstart = tc - 3; 4271573Srgrimes tclen = s - tcstart; 4281573Srgrimes tcend = s; 4291573Srgrimes 4308870Srgrimes iret = getent(&icap, &ilen, db_p, fd, tc, depth+1, 4311573Srgrimes NULL); 4321573Srgrimes newicap = icap; /* Put into a register. */ 4331573Srgrimes newilen = ilen; 4341573Srgrimes if (iret != 0) { 4351573Srgrimes /* an error */ 4361573Srgrimes if (iret < -1) { 4371573Srgrimes if (myfd) 43856698Sjasone (void)_close(fd); 4391573Srgrimes free(record); 4401573Srgrimes return (iret); 4411573Srgrimes } 4421573Srgrimes if (iret == 1) 4431573Srgrimes tc_not_resolved = 1; 4441573Srgrimes /* couldn't resolve tc */ 4451573Srgrimes if (iret == -1) { 4468870Srgrimes *(s - 1) = ':'; 4471573Srgrimes scan = s - 1; 4481573Srgrimes tc_not_resolved = 1; 4491573Srgrimes continue; 4508870Srgrimes 4511573Srgrimes } 4521573Srgrimes } 4531573Srgrimes /* not interested in name field of tc'ed record */ 4541573Srgrimes s = newicap; 4551573Srgrimes for (;;) 4561573Srgrimes if (*s == '\0') 4571573Srgrimes break; 4581573Srgrimes else 4591573Srgrimes if (*s++ == ':') 4601573Srgrimes break; 4611573Srgrimes newilen -= s - newicap; 4621573Srgrimes newicap = s; 4631573Srgrimes 4641573Srgrimes /* make sure interpolated record is `:'-terminated */ 4651573Srgrimes s += newilen; 4661573Srgrimes if (*(s-1) != ':') { 4671573Srgrimes *s = ':'; /* overwrite NUL with : */ 4681573Srgrimes newilen++; 4691573Srgrimes } 4701573Srgrimes 4711573Srgrimes /* 4721573Srgrimes * Make sure there's enough room to insert the 4731573Srgrimes * new record. 4741573Srgrimes */ 4751573Srgrimes diff = newilen - tclen; 4761573Srgrimes if (diff >= r_end - rp) { 4771573Srgrimes u_int pos, tcpos, tcposend; 4781573Srgrimes size_t newsize; 4791573Srgrimes 4801573Srgrimes pos = rp - record; 4811573Srgrimes newsize = r_end - record + diff + BFRAG; 4821573Srgrimes tcpos = tcstart - record; 4831573Srgrimes tcposend = tcend - record; 48439327Simp record = reallocf(record, newsize); 4851573Srgrimes if (record == NULL) { 4861573Srgrimes errno = ENOMEM; 4871573Srgrimes if (myfd) 48856698Sjasone (void)_close(fd); 4891573Srgrimes free(icap); 4901573Srgrimes return (-2); 4911573Srgrimes } 4921573Srgrimes r_end = record + newsize; 4931573Srgrimes rp = record + pos; 4941573Srgrimes tcstart = record + tcpos; 4951573Srgrimes tcend = record + tcposend; 4961573Srgrimes } 4971573Srgrimes 4981573Srgrimes /* 4991573Srgrimes * Insert tc'ed record into our record. 5001573Srgrimes */ 5011573Srgrimes s = tcstart + newilen; 5021573Srgrimes bcopy(tcend, s, rp - tcend); 5031573Srgrimes bcopy(newicap, tcstart, newilen); 5041573Srgrimes rp += diff; 5051573Srgrimes free(icap); 5061573Srgrimes 5071573Srgrimes /* 5081573Srgrimes * Start scan on `:' so next cgetcap works properly 5091573Srgrimes * (cgetcap always skips first field). 5101573Srgrimes */ 5111573Srgrimes scan = s-1; 5121573Srgrimes } 5138870Srgrimes 5141573Srgrimes } 5151573Srgrimes /* 5161573Srgrimes * Close file (if we opened it), give back any extra memory, and 5171573Srgrimes * return capability, length and success. 5181573Srgrimes */ 5191573Srgrimes if (myfd) 52056698Sjasone (void)_close(fd); 5211573Srgrimes *len = rp - record - 1; /* don't count NUL */ 5221573Srgrimes if (r_end > rp) 5238870Srgrimes if ((record = 52439327Simp reallocf(record, (size_t)(rp - record))) == NULL) { 5251573Srgrimes errno = ENOMEM; 5261573Srgrimes return (-2); 5271573Srgrimes } 5288870Srgrimes 5291573Srgrimes *cap = record; 5301573Srgrimes if (tc_not_resolved) 5311573Srgrimes return (1); 5321573Srgrimes return (0); 5338870Srgrimes} 5341573Srgrimes 5351573Srgrimesstatic int 536108312Salfredcdbget(DB *capdbp, char **bp, const char *name) 5371573Srgrimes{ 5381573Srgrimes DBT key, data; 539108312Salfred char *namebuf; 5401573Srgrimes 541108312Salfred namebuf = strdup(name); 542108312Salfred if (namebuf == NULL) 543108312Salfred return (-2); 544108312Salfred key.data = namebuf; 545108312Salfred key.size = strlen(namebuf); 5461573Srgrimes 5471573Srgrimes for (;;) { 5481573Srgrimes /* Get the reference. */ 5491573Srgrimes switch(capdbp->get(capdbp, &key, &data, 0)) { 5501573Srgrimes case -1: 551108312Salfred free(namebuf); 5521573Srgrimes return (-2); 5531573Srgrimes case 1: 554108312Salfred free(namebuf); 5551573Srgrimes return (-1); 5561573Srgrimes } 5571573Srgrimes 5581573Srgrimes /* If not an index to another record, leave. */ 5591573Srgrimes if (((char *)data.data)[0] != SHADOW) 5601573Srgrimes break; 5611573Srgrimes 5621573Srgrimes key.data = (char *)data.data + 1; 5631573Srgrimes key.size = data.size - 1; 5641573Srgrimes } 5658870Srgrimes 5661573Srgrimes *bp = (char *)data.data + 1; 567108312Salfred free(namebuf); 5681573Srgrimes return (((char *)(data.data))[0] == TCERR ? 1 : 0); 5691573Srgrimes} 5701573Srgrimes 5711573Srgrimes/* 5721573Srgrimes * Cgetmatch will return 0 if name is one of the names of the capability 5731573Srgrimes * record buf, -1 if not. 5741573Srgrimes */ 5751573Srgrimesint 57692925Simpcgetmatch(const char *buf, const char *name) 5771573Srgrimes{ 578108312Salfred const char *np, *bp; 5791573Srgrimes 5801573Srgrimes /* 5811573Srgrimes * Start search at beginning of record. 5821573Srgrimes */ 5831573Srgrimes bp = buf; 5841573Srgrimes for (;;) { 5851573Srgrimes /* 5861573Srgrimes * Try to match a record name. 5871573Srgrimes */ 5881573Srgrimes np = name; 5891573Srgrimes for (;;) 5901573Srgrimes if (*np == '\0') 5911573Srgrimes if (*bp == '|' || *bp == ':' || *bp == '\0') 5921573Srgrimes return (0); 5931573Srgrimes else 5941573Srgrimes break; 5951573Srgrimes else 5961573Srgrimes if (*bp++ != *np++) 5971573Srgrimes break; 5981573Srgrimes 5991573Srgrimes /* 6001573Srgrimes * Match failed, skip to next name in record. 6011573Srgrimes */ 6021573Srgrimes bp--; /* a '|' or ':' may have stopped the match */ 6031573Srgrimes for (;;) 6041573Srgrimes if (*bp == '\0' || *bp == ':') 6051573Srgrimes return (-1); /* match failed totally */ 6061573Srgrimes else 6071573Srgrimes if (*bp++ == '|') 6081573Srgrimes break; /* found next name */ 6091573Srgrimes } 6101573Srgrimes} 6111573Srgrimes 6121573Srgrimes 6131573Srgrimes 6141573Srgrimes 6151573Srgrimes 6161573Srgrimesint 61792925Simpcgetfirst(char **buf, char **db_array) 6181573Srgrimes{ 6191573Srgrimes (void)cgetclose(); 6201573Srgrimes return (cgetnext(buf, db_array)); 6211573Srgrimes} 6221573Srgrimes 6231573Srgrimesstatic FILE *pfp; 6241573Srgrimesstatic int slash; 6251573Srgrimesstatic char **dbp; 6261573Srgrimes 6271573Srgrimesint 62892925Simpcgetclose(void) 6291573Srgrimes{ 6301573Srgrimes if (pfp != NULL) { 6311573Srgrimes (void)fclose(pfp); 6321573Srgrimes pfp = NULL; 6331573Srgrimes } 6341573Srgrimes dbp = NULL; 6351573Srgrimes gottoprec = 0; 6361573Srgrimes slash = 0; 6371573Srgrimes return(0); 6381573Srgrimes} 6391573Srgrimes 6401573Srgrimes/* 6418870Srgrimes * Cgetnext() gets either the first or next entry in the logical database 6421573Srgrimes * specified by db_array. It returns 0 upon completion of the database, 1 6431573Srgrimes * upon returning an entry with more remaining, and -1 if an error occurs. 6441573Srgrimes */ 6451573Srgrimesint 64692925Simpcgetnext(char **bp, char **db_array) 6471573Srgrimes{ 6481573Srgrimes size_t len; 64969502Sgad int done, hadreaderr, i, savederrno, status; 6501573Srgrimes char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE]; 6511573Srgrimes u_int dummy; 6521573Srgrimes 6531573Srgrimes if (dbp == NULL) 6541573Srgrimes dbp = db_array; 6551573Srgrimes 6561573Srgrimes if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) { 6571573Srgrimes (void)cgetclose(); 6581573Srgrimes return (-1); 6591573Srgrimes } 6601573Srgrimes for(;;) { 6611573Srgrimes if (toprec && !gottoprec) { 6621573Srgrimes gottoprec = 1; 6631573Srgrimes line = toprec; 6641573Srgrimes } else { 6651573Srgrimes line = fgetln(pfp, &len); 6661573Srgrimes if (line == NULL && pfp) { 66769502Sgad hadreaderr = ferror(pfp); 66869502Sgad if (hadreaderr) 66969502Sgad savederrno = errno; 67069502Sgad fclose(pfp); 67169502Sgad pfp = NULL; 67269502Sgad if (hadreaderr) { 67369502Sgad cgetclose(); 67469502Sgad errno = savederrno; 6751573Srgrimes return (-1); 6761573Srgrimes } else { 6771573Srgrimes if (*++dbp == NULL) { 6781573Srgrimes (void)cgetclose(); 6791573Srgrimes return (0); 6801573Srgrimes } else if ((pfp = 6811573Srgrimes fopen(*dbp, "r")) == NULL) { 6821573Srgrimes (void)cgetclose(); 6831573Srgrimes return (-1); 6841573Srgrimes } else 6851573Srgrimes continue; 6861573Srgrimes } 6871573Srgrimes } else 6881573Srgrimes line[len - 1] = '\0'; 6891573Srgrimes if (len == 1) { 6901573Srgrimes slash = 0; 6911573Srgrimes continue; 6921573Srgrimes } 69352856Sache if (isspace((unsigned char)*line) || 6941573Srgrimes *line == ':' || *line == '#' || slash) { 6951573Srgrimes if (line[len - 2] == '\\') 6961573Srgrimes slash = 1; 6971573Srgrimes else 6981573Srgrimes slash = 0; 6991573Srgrimes continue; 7001573Srgrimes } 7011573Srgrimes if (line[len - 2] == '\\') 7021573Srgrimes slash = 1; 7031573Srgrimes else 7041573Srgrimes slash = 0; 7058870Srgrimes } 7061573Srgrimes 7071573Srgrimes 7088870Srgrimes /* 7091573Srgrimes * Line points to a name line. 7101573Srgrimes */ 7111573Srgrimes i = 0; 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