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