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