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