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