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