diff --git a/lib/libc/db/btree/bt_open.c b/lib/libc/db/btree/bt_open.c index ce3b8a1ecf1b..06bbd39f1842 100644 --- a/lib/libc/db/btree/bt_open.c +++ b/lib/libc/db/btree/bt_open.c @@ -1,451 +1,450 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)bt_open.c 8.10 (Berkeley) 8/17/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); /* * Implementation of btree access method for 4.4BSD. * * The design here was originally based on that of the btree access method * used in the Postgres database system at UC Berkeley. This implementation * is wholly independent of the Postgres code. */ #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" #include #include "btree.h" #ifdef DEBUG #undef MINPSIZE #define MINPSIZE 128 #endif static int byteorder(void); static int nroot(BTREE *); static int tmp(void); /* * __BT_OPEN -- Open a btree. * * Creates and fills a DB struct, and calls the routine that actually * opens the btree. * * Parameters: * fname: filename (NULL for in-memory trees) * flags: open flag bits * mode: open permission bits * b: BTREEINFO pointer * * Returns: * NULL on failure, pointer to DB on success. * */ DB * __bt_open(const char *fname, int flags, int mode, const BTREEINFO *openinfo, int dflags) { struct stat sb; BTMETA m; BTREE *t; BTREEINFO b; DB *dbp; pgno_t ncache; ssize_t nr; int machine_lorder, saved_errno; t = NULL; /* * Intention is to make sure all of the user's selections are okay * here and then use them without checking. Can't be complete, since * we don't know the right page size, lorder or flags until the backing * file is opened. Also, the file's page size can cause the cachesize * to change. */ machine_lorder = byteorder(); if (openinfo) { b = *openinfo; /* Flags: R_DUP. */ if (b.flags & ~(R_DUP)) goto einval; /* * Page size must be indx_t aligned and >= MINPSIZE. Default * page size is set farther on, based on the underlying file * transfer size. */ if (b.psize && (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 || b.psize & (sizeof(indx_t) - 1) )) goto einval; /* Minimum number of keys per page; absolute minimum is 2. */ if (b.minkeypage) { if (b.minkeypage < 2) goto einval; } else b.minkeypage = DEFMINKEYPAGE; /* If no comparison, use default comparison and prefix. */ if (b.compare == NULL) { b.compare = __bt_defcmp; if (b.prefix == NULL) b.prefix = __bt_defpfx; } if (b.lorder == 0) b.lorder = machine_lorder; } else { b.compare = __bt_defcmp; b.cachesize = 0; b.flags = 0; b.lorder = machine_lorder; b.minkeypage = DEFMINKEYPAGE; b.prefix = __bt_defpfx; b.psize = 0; } /* Check for the ubiquitous PDP-11. */ if (b.lorder != BIG_ENDIAN && b.lorder != LITTLE_ENDIAN) goto einval; /* Allocate and initialize DB and BTREE structures. */ if ((t = (BTREE *)calloc(1, sizeof(BTREE))) == NULL) goto err; t->bt_fd = -1; /* Don't close unopened fd on error. */ t->bt_lorder = b.lorder; t->bt_order = NOT; t->bt_cmp = b.compare; t->bt_pfx = b.prefix; t->bt_rfd = -1; if ((t->bt_dbp = dbp = (DB *)calloc(1, sizeof(DB))) == NULL) goto err; if (t->bt_lorder != machine_lorder) F_SET(t, B_NEEDSWAP); dbp->type = DB_BTREE; dbp->internal = t; dbp->close = __bt_close; dbp->del = __bt_delete; dbp->fd = __bt_fd; dbp->get = __bt_get; dbp->put = __bt_put; dbp->seq = __bt_seq; dbp->sync = __bt_sync; /* * If no file name was supplied, this is an in-memory btree and we * open a backing temporary file. Otherwise, it's a disk-based tree. */ if (fname) { switch (flags & O_ACCMODE) { case O_RDONLY: F_SET(t, B_RDONLY); break; case O_RDWR: break; case O_WRONLY: default: goto einval; } if ((t->bt_fd = _open(fname, flags | O_CLOEXEC, mode)) < 0) goto err; } else { if ((flags & O_ACCMODE) != O_RDWR) goto einval; if ((t->bt_fd = tmp()) == -1) goto err; F_SET(t, B_INMEM); } if (_fstat(t->bt_fd, &sb)) goto err; if (sb.st_size) { if ((nr = _read(t->bt_fd, &m, sizeof(BTMETA))) < 0) goto err; if (nr != sizeof(BTMETA)) goto eftype; /* * Read in the meta-data. This can change the notion of what * the lorder, page size and flags are, and, when the page size * changes, the cachesize value can change too. If the user * specified the wrong byte order for an existing database, we * don't bother to return an error, we just clear the NEEDSWAP * bit. */ if (m.magic == BTREEMAGIC) F_CLR(t, B_NEEDSWAP); else { F_SET(t, B_NEEDSWAP); M_32_SWAP(m.magic); M_32_SWAP(m.version); M_32_SWAP(m.psize); M_32_SWAP(m.free); M_32_SWAP(m.nrecs); M_32_SWAP(m.flags); } if (m.magic != BTREEMAGIC || m.version != BTREEVERSION) goto eftype; if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 || m.psize & (sizeof(indx_t) - 1) ) goto eftype; if (m.flags & ~SAVEMETA) goto eftype; b.psize = m.psize; F_SET(t, m.flags); t->bt_free = m.free; t->bt_nrecs = m.nrecs; } else { /* * Set the page size to the best value for I/O to this file. * Don't overflow the page offset type. */ if (b.psize == 0) { b.psize = sb.st_blksize; if (b.psize < MINPSIZE) b.psize = MINPSIZE; if (b.psize > MAX_PAGE_OFFSET + 1) b.psize = MAX_PAGE_OFFSET + 1; } /* Set flag if duplicates permitted. */ if (!(b.flags & R_DUP)) F_SET(t, B_NODUPS); t->bt_free = P_INVALID; t->bt_nrecs = 0; F_SET(t, B_METADIRTY); } t->bt_psize = b.psize; /* Set the cache size; must be a multiple of the page size. */ if (b.cachesize && b.cachesize & (b.psize - 1) ) b.cachesize += (~b.cachesize & (b.psize - 1) ) + 1; if (b.cachesize < b.psize * MINCACHE) b.cachesize = b.psize * MINCACHE; /* Calculate number of pages to cache. */ ncache = howmany(b.cachesize, t->bt_psize); /* * The btree data structure requires that at least two keys can fit on * a page, but other than that there's no fixed requirement. The user * specified a minimum number per page, and we translated that into the * number of bytes a key/data pair can use before being placed on an * overflow page. This calculation includes the page header, the size * of the index referencing the leaf item and the size of the leaf item * structure. Also, don't let the user specify a minkeypage such that * a key/data pair won't fit even if both key and data are on overflow * pages. */ t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage - (sizeof(indx_t) + NBLEAFDBT(0, 0)); if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t)) t->bt_ovflsize = NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t); /* Initialize the buffer pool. */ if ((t->bt_mp = mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL) goto err; if (!F_ISSET(t, B_INMEM)) mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t); /* Create a root page if new tree. */ if (nroot(t) == RET_ERROR) goto err; /* Global flags. */ if (dflags & DB_LOCK) F_SET(t, B_DB_LOCK); if (dflags & DB_SHMEM) F_SET(t, B_DB_SHMEM); if (dflags & DB_TXN) F_SET(t, B_DB_TXN); return (dbp); einval: errno = EINVAL; goto err; eftype: errno = EFTYPE; goto err; err: saved_errno = errno; if (t) { if (t->bt_dbp) free(t->bt_dbp); if (t->bt_fd != -1) (void)_close(t->bt_fd); free(t); } errno = saved_errno; return (NULL); } /* * NROOT -- Create the root of a new tree. * * Parameters: * t: tree * * Returns: * RET_ERROR, RET_SUCCESS */ static int nroot(BTREE *t) { PAGE *meta, *root; pgno_t npg; if ((root = mpool_get(t->bt_mp, 1, 0)) != NULL) { if (root->lower == 0 && root->pgno == 0 && root->linp[0] == 0) { mpool_delete(t->bt_mp, root); errno = EINVAL; } else { mpool_put(t->bt_mp, root, 0); return (RET_SUCCESS); } } if (errno != EINVAL) /* It's OK to not exist. */ return (RET_ERROR); errno = 0; if ((meta = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL) return (RET_ERROR); if ((root = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL) return (RET_ERROR); if (npg != P_ROOT) return (RET_ERROR); root->pgno = npg; root->prevpg = root->nextpg = P_INVALID; root->lower = BTDATAOFF; root->upper = t->bt_psize; root->flags = P_BLEAF; memset(meta, 0, t->bt_psize); mpool_put(t->bt_mp, meta, MPOOL_DIRTY); mpool_put(t->bt_mp, root, MPOOL_DIRTY); return (RET_SUCCESS); } static int tmp(void) { sigset_t set, oset; int fd, len; - char *envtmp = NULL; + char *envtmp; char path[MAXPATHLEN]; - if (issetugid() == 0) - envtmp = getenv("TMPDIR"); + envtmp = secure_getenv("TMPDIR"); len = snprintf(path, sizeof(path), "%s/bt.XXXXXXXXXX", envtmp ? envtmp : "/tmp"); if (len < 0 || len >= (int)sizeof(path)) { errno = ENAMETOOLONG; return(-1); } (void)sigfillset(&set); (void)__libc_sigprocmask(SIG_BLOCK, &set, &oset); if ((fd = mkostemp(path, O_CLOEXEC)) != -1) (void)unlink(path); (void)__libc_sigprocmask(SIG_SETMASK, &oset, NULL); return(fd); } static int byteorder(void) { u_int32_t x; u_char *p; x = 0x01020304; p = (u_char *)&x; switch (*p) { case 1: return (BIG_ENDIAN); case 4: return (LITTLE_ENDIAN); default: return (0); } } int __bt_fd(const DB *dbp) { BTREE *t; t = dbp->internal; /* Toss any page pinned across calls. */ if (t->bt_pinned != NULL) { mpool_put(t->bt_mp, t->bt_pinned, 0); t->bt_pinned = NULL; } /* In-memory database can't have a file descriptor. */ if (F_ISSET(t, B_INMEM)) { errno = ENOENT; return (-1); } return (t->bt_fd); } diff --git a/lib/libc/db/hash/hash_page.c b/lib/libc/db/hash/hash_page.c index fba854b51f33..afcda6321133 100644 --- a/lib/libc/db/hash/hash_page.c +++ b/lib/libc/db/hash/hash_page.c @@ -1,942 +1,941 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Margo Seltzer. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)hash_page.c 8.7 (Berkeley) 8/16/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); /* * PACKAGE: hashing * * DESCRIPTION: * Page manipulation for hashing package. * * ROUTINES: * * External * __get_page * __add_ovflpage * Internal * overflow_page * open_temp */ #include "namespace.h" #include #include #include #include #include #include #include #include #ifdef DEBUG #include #endif #include "un-namespace.h" #include "libc_private.h" #include #include "hash.h" #include "page.h" #include "extern.h" static u_int32_t *fetch_bitmap(HTAB *, int); static u_int32_t first_free(u_int32_t); static int open_temp(HTAB *); static u_int16_t overflow_page(HTAB *); static void putpair(char *, const DBT *, const DBT *); static void squeeze_key(u_int16_t *, const DBT *, const DBT *); static int ugly_split(HTAB *, u_int32_t, BUFHEAD *, BUFHEAD *, int, int); #define PAGE_INIT(P) { \ ((u_int16_t *)(P))[0] = 0; \ ((u_int16_t *)(P))[1] = hashp->BSIZE - 3 * sizeof(u_int16_t); \ ((u_int16_t *)(P))[2] = hashp->BSIZE; \ } /* * This is called AFTER we have verified that there is room on the page for * the pair (PAIRFITS has returned true) so we go right ahead and start moving * stuff on. */ static void putpair(char *p, const DBT *key, const DBT *val) { u_int16_t *bp, n, off; bp = (u_int16_t *)p; /* Enter the key first. */ n = bp[0]; off = OFFSET(bp) - key->size; memmove(p + off, key->data, key->size); bp[++n] = off; /* Now the data. */ off -= val->size; memmove(p + off, val->data, val->size); bp[++n] = off; /* Adjust page info. */ bp[0] = n; bp[n + 1] = off - ((n + 3) * sizeof(u_int16_t)); bp[n + 2] = off; } /* * Returns: * 0 OK * -1 error */ int __delpair(HTAB *hashp, BUFHEAD *bufp, int ndx) { u_int16_t *bp, newoff, pairlen; int n; bp = (u_int16_t *)bufp->page; n = bp[0]; if (bp[ndx + 1] < REAL_KEY) return (__big_delete(hashp, bufp)); if (ndx != 1) newoff = bp[ndx - 1]; else newoff = hashp->BSIZE; pairlen = newoff - bp[ndx + 1]; if (ndx != (n - 1)) { /* Hard Case -- need to shuffle keys */ int i; char *src = bufp->page + (int)OFFSET(bp); char *dst = src + (int)pairlen; memmove(dst, src, bp[ndx + 1] - OFFSET(bp)); /* Now adjust the pointers */ for (i = ndx + 2; i <= n; i += 2) { if (bp[i + 1] == OVFLPAGE) { bp[i - 2] = bp[i]; bp[i - 1] = bp[i + 1]; } else { bp[i - 2] = bp[i] + pairlen; bp[i - 1] = bp[i + 1] + pairlen; } } if (ndx == hashp->cndx) { /* * We just removed pair we were "pointing" to. * By moving back the cndx we ensure subsequent * hash_seq() calls won't skip over any entries. */ hashp->cndx -= 2; } } /* Finally adjust the page data */ bp[n] = OFFSET(bp) + pairlen; bp[n - 1] = bp[n + 1] + pairlen + 2 * sizeof(u_int16_t); bp[0] = n - 2; hashp->NKEYS--; bufp->flags |= BUF_MOD; return (0); } /* * Returns: * 0 ==> OK * -1 ==> Error */ int __split_page(HTAB *hashp, u_int32_t obucket, u_int32_t nbucket) { BUFHEAD *new_bufp, *old_bufp; u_int16_t *ino; char *np; DBT key, val; int n, ndx, retval; u_int16_t copyto, diff, off, moved; char *op; copyto = (u_int16_t)hashp->BSIZE; off = (u_int16_t)hashp->BSIZE; old_bufp = __get_buf(hashp, obucket, NULL, 0); if (old_bufp == NULL) return (-1); new_bufp = __get_buf(hashp, nbucket, NULL, 0); if (new_bufp == NULL) return (-1); old_bufp->flags |= (BUF_MOD | BUF_PIN); new_bufp->flags |= (BUF_MOD | BUF_PIN); ino = (u_int16_t *)(op = old_bufp->page); np = new_bufp->page; moved = 0; for (n = 1, ndx = 1; n < ino[0]; n += 2) { if (ino[n + 1] < REAL_KEY) { retval = ugly_split(hashp, obucket, old_bufp, new_bufp, (int)copyto, (int)moved); old_bufp->flags &= ~BUF_PIN; new_bufp->flags &= ~BUF_PIN; return (retval); } key.data = (u_char *)op + ino[n]; key.size = off - ino[n]; if (__call_hash(hashp, key.data, key.size) == obucket) { /* Don't switch page */ diff = copyto - off; if (diff) { copyto = ino[n + 1] + diff; memmove(op + copyto, op + ino[n + 1], off - ino[n + 1]); ino[ndx] = copyto + ino[n] - ino[n + 1]; ino[ndx + 1] = copyto; } else copyto = ino[n + 1]; ndx += 2; } else { /* Switch page */ val.data = (u_char *)op + ino[n + 1]; val.size = ino[n] - ino[n + 1]; putpair(np, &key, &val); moved += 2; } off = ino[n + 1]; } /* Now clean up the page */ ino[0] -= moved; FREESPACE(ino) = copyto - sizeof(u_int16_t) * (ino[0] + 3); OFFSET(ino) = copyto; #ifdef DEBUG3 (void)fprintf(stderr, "split %d/%d\n", ((u_int16_t *)np)[0] / 2, ((u_int16_t *)op)[0] / 2); #endif /* unpin both pages */ old_bufp->flags &= ~BUF_PIN; new_bufp->flags &= ~BUF_PIN; return (0); } /* * Called when we encounter an overflow or big key/data page during split * handling. This is special cased since we have to begin checking whether * the key/data pairs fit on their respective pages and because we may need * overflow pages for both the old and new pages. * * The first page might be a page with regular key/data pairs in which case * we have a regular overflow condition and just need to go on to the next * page or it might be a big key/data pair in which case we need to fix the * big key/data pair. * * Returns: * 0 ==> success * -1 ==> failure */ static int ugly_split(HTAB *hashp, u_int32_t obucket, /* Same as __split_page. */ BUFHEAD *old_bufp, BUFHEAD *new_bufp, int copyto, /* First byte on page which contains key/data values. */ int moved) /* Number of pairs moved to new page. */ { BUFHEAD *bufp; /* Buffer header for ino */ u_int16_t *ino; /* Page keys come off of */ u_int16_t *np; /* New page */ u_int16_t *op; /* Page keys go on to if they aren't moving */ BUFHEAD *last_bfp; /* Last buf header OVFL needing to be freed */ DBT key, val; SPLIT_RETURN ret; u_int16_t n, off, ov_addr, scopyto; char *cino; /* Character value of ino */ bufp = old_bufp; ino = (u_int16_t *)old_bufp->page; np = (u_int16_t *)new_bufp->page; op = (u_int16_t *)old_bufp->page; last_bfp = NULL; scopyto = (u_int16_t)copyto; /* ANSI */ n = ino[0] - 1; while (n < ino[0]) { if (ino[2] < REAL_KEY && ino[2] != OVFLPAGE) { if (__big_split(hashp, old_bufp, new_bufp, bufp, bufp->addr, obucket, &ret)) return (-1); old_bufp = ret.oldp; if (!old_bufp) return (-1); op = (u_int16_t *)old_bufp->page; new_bufp = ret.newp; if (!new_bufp) return (-1); np = (u_int16_t *)new_bufp->page; bufp = ret.nextp; if (!bufp) return (0); cino = (char *)bufp->page; ino = (u_int16_t *)cino; last_bfp = ret.nextp; } else if (ino[n + 1] == OVFLPAGE) { ov_addr = ino[n]; /* * Fix up the old page -- the extra 2 are the fields * which contained the overflow information. */ ino[0] -= (moved + 2); FREESPACE(ino) = scopyto - sizeof(u_int16_t) * (ino[0] + 3); OFFSET(ino) = scopyto; bufp = __get_buf(hashp, ov_addr, bufp, 0); if (!bufp) return (-1); ino = (u_int16_t *)bufp->page; n = 1; scopyto = hashp->BSIZE; moved = 0; if (last_bfp) __free_ovflpage(hashp, last_bfp); last_bfp = bufp; } /* Move regular sized pairs of there are any */ off = hashp->BSIZE; for (n = 1; (n < ino[0]) && (ino[n + 1] >= REAL_KEY); n += 2) { cino = (char *)ino; key.data = (u_char *)cino + ino[n]; key.size = off - ino[n]; val.data = (u_char *)cino + ino[n + 1]; val.size = ino[n] - ino[n + 1]; off = ino[n + 1]; if (__call_hash(hashp, key.data, key.size) == obucket) { /* Keep on old page */ if (PAIRFITS(op, (&key), (&val))) putpair((char *)op, &key, &val); else { old_bufp = __add_ovflpage(hashp, old_bufp); if (!old_bufp) return (-1); op = (u_int16_t *)old_bufp->page; putpair((char *)op, &key, &val); } old_bufp->flags |= BUF_MOD; } else { /* Move to new page */ if (PAIRFITS(np, (&key), (&val))) putpair((char *)np, &key, &val); else { new_bufp = __add_ovflpage(hashp, new_bufp); if (!new_bufp) return (-1); np = (u_int16_t *)new_bufp->page; putpair((char *)np, &key, &val); } new_bufp->flags |= BUF_MOD; } } } if (last_bfp) __free_ovflpage(hashp, last_bfp); return (0); } /* * Add the given pair to the page * * Returns: * 0 ==> OK * 1 ==> failure */ int __addel(HTAB *hashp, BUFHEAD *bufp, const DBT *key, const DBT *val) { u_int16_t *bp, *sop; int do_expand; bp = (u_int16_t *)bufp->page; do_expand = 0; while (bp[0] && (bp[2] < REAL_KEY || bp[bp[0]] < REAL_KEY)) /* Exception case */ if (bp[2] == FULL_KEY_DATA && bp[0] == 2) /* This is the last page of a big key/data pair and we need to add another page */ break; else if (bp[2] < REAL_KEY && bp[bp[0]] != OVFLPAGE) { bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1); bp = (u_int16_t *)bufp->page; } else if (bp[bp[0]] != OVFLPAGE) { /* Short key/data pairs, no more pages */ break; } else { /* Try to squeeze key on this page */ if (bp[2] >= REAL_KEY && FREESPACE(bp) >= PAIRSIZE(key, val)) { squeeze_key(bp, key, val); goto stats; } else { bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0); if (!bufp) return (-1); bp = (u_int16_t *)bufp->page; } } if (PAIRFITS(bp, key, val)) putpair(bufp->page, key, val); else { do_expand = 1; bufp = __add_ovflpage(hashp, bufp); if (!bufp) return (-1); sop = (u_int16_t *)bufp->page; if (PAIRFITS(sop, key, val)) putpair((char *)sop, key, val); else if (__big_insert(hashp, bufp, key, val)) return (-1); } stats: bufp->flags |= BUF_MOD; /* * If the average number of keys per bucket exceeds the fill factor, * expand the table. */ hashp->NKEYS++; if (do_expand || (hashp->NKEYS / (hashp->MAX_BUCKET + 1) > hashp->FFACTOR)) return (__expand_table(hashp)); return (0); } /* * * Returns: * pointer on success * NULL on error */ BUFHEAD * __add_ovflpage(HTAB *hashp, BUFHEAD *bufp) { u_int16_t *sp, ndx, ovfl_num; #ifdef DEBUG1 int tmp1, tmp2; #endif sp = (u_int16_t *)bufp->page; /* Check if we are dynamically determining the fill factor */ if (hashp->FFACTOR == DEF_FFACTOR) { hashp->FFACTOR = sp[0] >> 1; if (hashp->FFACTOR < MIN_FFACTOR) hashp->FFACTOR = MIN_FFACTOR; } bufp->flags |= BUF_MOD; ovfl_num = overflow_page(hashp); #ifdef DEBUG1 tmp1 = bufp->addr; tmp2 = bufp->ovfl ? bufp->ovfl->addr : 0; #endif if (!ovfl_num || !(bufp->ovfl = __get_buf(hashp, ovfl_num, bufp, 1))) return (NULL); bufp->ovfl->flags |= BUF_MOD; #ifdef DEBUG1 (void)fprintf(stderr, "ADDOVFLPAGE: %d->ovfl was %d is now %d\n", tmp1, tmp2, bufp->ovfl->addr); #endif ndx = sp[0]; /* * Since a pair is allocated on a page only if there's room to add * an overflow page, we know that the OVFL information will fit on * the page. */ sp[ndx + 4] = OFFSET(sp); sp[ndx + 3] = FREESPACE(sp) - OVFLSIZE; sp[ndx + 1] = ovfl_num; sp[ndx + 2] = OVFLPAGE; sp[0] = ndx + 2; #ifdef HASH_STATISTICS hash_overflows++; #endif return (bufp->ovfl); } /* * Returns: * 0 indicates SUCCESS * -1 indicates FAILURE */ int __get_page(HTAB *hashp, char *p, u_int32_t bucket, int is_bucket, int is_disk, int is_bitmap) { int fd, page, size, rsize; u_int16_t *bp; fd = hashp->fp; size = hashp->BSIZE; if ((fd == -1) || !is_disk) { PAGE_INIT(p); return (0); } if (is_bucket) page = BUCKET_TO_PAGE(bucket); else page = OADDR_TO_PAGE(bucket); if ((rsize = pread(fd, p, size, (off_t)page << hashp->BSHIFT)) == -1) return (-1); bp = (u_int16_t *)p; if (!rsize) bp[0] = 0; /* We hit the EOF, so initialize a new page */ else if (rsize != size) { errno = EFTYPE; return (-1); } if (!is_bitmap && !bp[0]) { PAGE_INIT(p); } else if (hashp->LORDER != BYTE_ORDER) { int i, max; if (is_bitmap) { max = hashp->BSIZE >> 2; /* divide by 4 */ for (i = 0; i < max; i++) M_32_SWAP(((int *)p)[i]); } else { M_16_SWAP(bp[0]); max = bp[0] + 2; for (i = 1; i <= max; i++) M_16_SWAP(bp[i]); } } return (0); } /* * Write page p to disk * * Returns: * 0 ==> OK * -1 ==>failure */ int __put_page(HTAB *hashp, char *p, u_int32_t bucket, int is_bucket, int is_bitmap) { int fd, page, size; ssize_t wsize; char pbuf[MAX_BSIZE]; size = hashp->BSIZE; if ((hashp->fp == -1) && open_temp(hashp)) return (-1); fd = hashp->fp; if (hashp->LORDER != BYTE_ORDER) { int i, max; memcpy(pbuf, p, size); if (is_bitmap) { max = hashp->BSIZE >> 2; /* divide by 4 */ for (i = 0; i < max; i++) M_32_SWAP(((int *)pbuf)[i]); } else { uint16_t *bp = (uint16_t *)(void *)pbuf; max = bp[0] + 2; for (i = 0; i <= max; i++) M_16_SWAP(bp[i]); } p = pbuf; } if (is_bucket) page = BUCKET_TO_PAGE(bucket); else page = OADDR_TO_PAGE(bucket); if ((wsize = pwrite(fd, p, size, (off_t)page << hashp->BSHIFT)) == -1) /* Errno is set */ return (-1); if (wsize != size) { errno = EFTYPE; return (-1); } return (0); } #define BYTE_MASK ((1 << INT_BYTE_SHIFT) -1) /* * Initialize a new bitmap page. Bitmap pages are left in memory * once they are read in. */ int __ibitmap(HTAB *hashp, int pnum, int nbits, int ndx) { u_int32_t *ip; int clearbytes, clearints; if ((ip = (u_int32_t *)malloc(hashp->BSIZE)) == NULL) return (1); hashp->nmaps++; clearints = ((nbits - 1) >> INT_BYTE_SHIFT) + 1; clearbytes = clearints << INT_TO_BYTE; (void)memset((char *)ip, 0, clearbytes); (void)memset(((char *)ip) + clearbytes, 0xFF, hashp->BSIZE - clearbytes); ip[clearints - 1] = ALL_SET << (nbits & BYTE_MASK); SETBIT(ip, 0); hashp->BITMAPS[ndx] = (u_int16_t)pnum; hashp->mapp[ndx] = ip; return (0); } static u_int32_t first_free(u_int32_t map) { u_int32_t i, mask; mask = 0x1; for (i = 0; i < BITS_PER_MAP; i++) { if (!(mask & map)) return (i); mask = mask << 1; } return (i); } static u_int16_t overflow_page(HTAB *hashp) { u_int32_t *freep; int max_free, offset, splitnum; u_int16_t addr; int bit, first_page, free_bit, free_page, i, in_use_bits, j; #ifdef DEBUG2 int tmp1, tmp2; #endif splitnum = hashp->OVFL_POINT; max_free = hashp->SPARES[splitnum]; free_page = (max_free - 1) >> (hashp->BSHIFT + BYTE_SHIFT); free_bit = (max_free - 1) & ((hashp->BSIZE << BYTE_SHIFT) - 1); /* Look through all the free maps to find the first free block */ first_page = hashp->LAST_FREED >>(hashp->BSHIFT + BYTE_SHIFT); for ( i = first_page; i <= free_page; i++ ) { if (!(freep = (u_int32_t *)hashp->mapp[i]) && !(freep = fetch_bitmap(hashp, i))) return (0); if (i == free_page) in_use_bits = free_bit; else in_use_bits = (hashp->BSIZE << BYTE_SHIFT) - 1; if (i == first_page) { bit = hashp->LAST_FREED & ((hashp->BSIZE << BYTE_SHIFT) - 1); j = bit / BITS_PER_MAP; bit = rounddown2(bit, BITS_PER_MAP); } else { bit = 0; j = 0; } for (; bit <= in_use_bits; j++, bit += BITS_PER_MAP) if (freep[j] != ALL_SET) goto found; } /* No Free Page Found */ hashp->LAST_FREED = hashp->SPARES[splitnum]; hashp->SPARES[splitnum]++; offset = hashp->SPARES[splitnum] - (splitnum ? hashp->SPARES[splitnum - 1] : 0); #define OVMSG "HASH: Out of overflow pages. Increase page size\n" if (offset > SPLITMASK) { if (++splitnum >= NCACHED) { (void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); errno = EFBIG; return (0); } hashp->OVFL_POINT = splitnum; hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; hashp->SPARES[splitnum-1]--; offset = 1; } /* Check if we need to allocate a new bitmap page */ if (free_bit == (hashp->BSIZE << BYTE_SHIFT) - 1) { free_page++; if (free_page >= NCACHED) { (void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); errno = EFBIG; return (0); } /* * This is tricky. The 1 indicates that you want the new page * allocated with 1 clear bit. Actually, you are going to * allocate 2 pages from this map. The first is going to be * the map page, the second is the overflow page we were * looking for. The init_bitmap routine automatically, sets * the first bit of itself to indicate that the bitmap itself * is in use. We would explicitly set the second bit, but * don't have to if we tell init_bitmap not to leave it clear * in the first place. */ if (__ibitmap(hashp, (int)OADDR_OF(splitnum, offset), 1, free_page)) return (0); hashp->SPARES[splitnum]++; #ifdef DEBUG2 free_bit = 2; #endif offset++; if (offset > SPLITMASK) { if (++splitnum >= NCACHED) { (void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); errno = EFBIG; return (0); } hashp->OVFL_POINT = splitnum; hashp->SPARES[splitnum] = hashp->SPARES[splitnum-1]; hashp->SPARES[splitnum-1]--; offset = 0; } } else { /* * Free_bit addresses the last used bit. Bump it to address * the first available bit. */ free_bit++; SETBIT(freep, free_bit); } /* Calculate address of the new overflow page */ addr = OADDR_OF(splitnum, offset); #ifdef DEBUG2 (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", addr, free_bit, free_page); #endif return (addr); found: bit = bit + first_free(freep[j]); SETBIT(freep, bit); #ifdef DEBUG2 tmp1 = bit; tmp2 = i; #endif /* * Bits are addressed starting with 0, but overflow pages are addressed * beginning at 1. Bit is a bit addressnumber, so we need to increment * it to convert it to a page number. */ bit = 1 + bit + (i * (hashp->BSIZE << BYTE_SHIFT)); if (bit >= hashp->LAST_FREED) hashp->LAST_FREED = bit - 1; /* Calculate the split number for this page */ for (i = 0; (i < splitnum) && (bit > hashp->SPARES[i]); i++); offset = (i ? bit - hashp->SPARES[i - 1] : bit); if (offset >= SPLITMASK) { (void)_write(STDERR_FILENO, OVMSG, sizeof(OVMSG) - 1); errno = EFBIG; return (0); /* Out of overflow pages */ } addr = OADDR_OF(i, offset); #ifdef DEBUG2 (void)fprintf(stderr, "OVERFLOW_PAGE: ADDR: %d BIT: %d PAGE %d\n", addr, tmp1, tmp2); #endif /* Allocate and return the overflow page */ return (addr); } /* * Mark this overflow page as free. */ void __free_ovflpage(HTAB *hashp, BUFHEAD *obufp) { u_int16_t addr; u_int32_t *freep; int bit_address, free_page, free_bit; u_int16_t ndx; addr = obufp->addr; #ifdef DEBUG1 (void)fprintf(stderr, "Freeing %d\n", addr); #endif ndx = (((u_int16_t)addr) >> SPLITSHIFT); bit_address = (ndx ? hashp->SPARES[ndx - 1] : 0) + (addr & SPLITMASK) - 1; if (bit_address < hashp->LAST_FREED) hashp->LAST_FREED = bit_address; free_page = (bit_address >> (hashp->BSHIFT + BYTE_SHIFT)); free_bit = bit_address & ((hashp->BSIZE << BYTE_SHIFT) - 1); if (!(freep = hashp->mapp[free_page])) freep = fetch_bitmap(hashp, free_page); #ifdef DEBUG /* * This had better never happen. It means we tried to read a bitmap * that has already had overflow pages allocated off it, and we * failed to read it from the file. */ if (!freep) assert(0); #endif CLRBIT(freep, free_bit); #ifdef DEBUG2 (void)fprintf(stderr, "FREE_OVFLPAGE: ADDR: %d BIT: %d PAGE %d\n", obufp->addr, free_bit, free_page); #endif __reclaim_buf(hashp, obufp); } /* * Returns: * 0 success * -1 failure */ static int open_temp(HTAB *hashp) { sigset_t set, oset; int len; - char *envtmp = NULL; + char *envtmp; char path[MAXPATHLEN]; - if (issetugid() == 0) - envtmp = getenv("TMPDIR"); + envtmp = secure_getenv("TMPDIR"); len = snprintf(path, sizeof(path), "%s/_hash.XXXXXX", envtmp ? envtmp : "/tmp"); if (len < 0 || len >= (int)sizeof(path)) { errno = ENAMETOOLONG; return (-1); } /* Block signals; make sure file goes away at process exit. */ (void)sigfillset(&set); (void)__libc_sigprocmask(SIG_BLOCK, &set, &oset); if ((hashp->fp = mkostemp(path, O_CLOEXEC)) != -1) (void)unlink(path); (void)__libc_sigprocmask(SIG_SETMASK, &oset, (sigset_t *)NULL); return (hashp->fp != -1 ? 0 : -1); } /* * We have to know that the key will fit, but the last entry on the page is * an overflow pair, so we need to shift things. */ static void squeeze_key(u_int16_t *sp, const DBT *key, const DBT *val) { char *p; u_int16_t free_space, n, off, pageno; p = (char *)sp; n = sp[0]; free_space = FREESPACE(sp); off = OFFSET(sp); pageno = sp[n - 1]; off -= key->size; sp[n - 1] = off; memmove(p + off, key->data, key->size); off -= val->size; sp[n] = off; memmove(p + off, val->data, val->size); sp[0] = n + 2; sp[n + 1] = pageno; sp[n + 2] = OVFLPAGE; FREESPACE(sp) = free_space - PAIRSIZE(key, val); OFFSET(sp) = off; } static u_int32_t * fetch_bitmap(HTAB *hashp, int ndx) { if (ndx >= hashp->nmaps) return (NULL); if ((hashp->mapp[ndx] = (u_int32_t *)malloc(hashp->BSIZE)) == NULL) return (NULL); if (__get_page(hashp, (char *)hashp->mapp[ndx], hashp->BITMAPS[ndx], 0, 1, 1)) { free(hashp->mapp[ndx]); return (NULL); } return (hashp->mapp[ndx]); } #ifdef DEBUG4 int print_chain(int addr) { BUFHEAD *bufp; short *bp, oaddr; (void)fprintf(stderr, "%d ", addr); bufp = __get_buf(hashp, addr, NULL, 0); bp = (short *)bufp->page; while (bp[0] && ((bp[bp[0]] == OVFLPAGE) || ((bp[0] > 2) && bp[2] < REAL_KEY))) { oaddr = bp[bp[0] - 1]; (void)fprintf(stderr, "%d ", (int)oaddr); bufp = __get_buf(hashp, (int)oaddr, bufp, 0); bp = (short *)bufp->page; } (void)fprintf(stderr, "\n"); } #endif diff --git a/lib/libc/gen/fstab.c b/lib/libc/gen/fstab.c index 3813202afb15..718373931757 100644 --- a/lib/libc/gen/fstab.c +++ b/lib/libc/gen/fstab.c @@ -1,302 +1,298 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1980, 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __SCCSID("@(#)fstab.c 8.1 (Berkeley) 6/4/93"); __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" static FILE *_fs_fp; static struct fstab _fs_fstab; static int LineNo = 0; static char *path_fstab; static char fstab_path[PATH_MAX]; static int fsp_set = 0; static void error(int); static void fixfsfile(void); static int fstabscan(void); void setfstab(const char *file) { if (file == NULL) { path_fstab = _PATH_FSTAB; } else { strncpy(fstab_path, file, PATH_MAX); fstab_path[PATH_MAX - 1] = '\0'; path_fstab = fstab_path; } fsp_set = 1; return; } const char * getfstab(void) { if (fsp_set) return (path_fstab); else return (_PATH_FSTAB); } static void fixfsfile(void) { static char buf[sizeof(_PATH_DEV) + MNAMELEN]; struct stat sb; struct statfs sf; if (_fs_fstab.fs_file != NULL && strcmp(_fs_fstab.fs_file, "/") != 0) return; if (statfs("/", &sf) != 0) return; if (sf.f_mntfromname[0] == '/') buf[0] = '\0'; else strcpy(buf, _PATH_DEV); strcat(buf, sf.f_mntfromname); if (stat(buf, &sb) != 0 || (!S_ISBLK(sb.st_mode) && !S_ISCHR(sb.st_mode))) return; _fs_fstab.fs_spec = buf; } static int fstabscan(void) { char *cp, *p; #define MAXLINELENGTH 1024 static char line[MAXLINELENGTH]; char subline[MAXLINELENGTH]; int typexx; for (;;) { if (!(p = fgets(line, sizeof(line), _fs_fp))) return (0); /* OLD_STYLE_FSTAB */ ++LineNo; if (*line == '#' || *line == '\n') continue; if (!strpbrk(p, " \t")) { _fs_fstab.fs_spec = strsep(&p, ":\n"); _fs_fstab.fs_file = strsep(&p, ":\n"); fixfsfile(); _fs_fstab.fs_type = strsep(&p, ":\n"); if (_fs_fstab.fs_type) { if (!strcmp(_fs_fstab.fs_type, FSTAB_XX)) continue; _fs_fstab.fs_mntops = _fs_fstab.fs_type; _fs_fstab.fs_vfstype = strcmp(_fs_fstab.fs_type, FSTAB_SW) ? "ufs" : "swap"; if ((cp = strsep(&p, ":\n")) != NULL) { _fs_fstab.fs_freq = atoi(cp); if ((cp = strsep(&p, ":\n")) != NULL) { _fs_fstab.fs_passno = atoi(cp); return (1); } } } goto bad; } /* OLD_STYLE_FSTAB */ while ((cp = strsep(&p, " \t\n")) != NULL && *cp == '\0') ; _fs_fstab.fs_spec = cp; if (_fs_fstab.fs_spec == NULL || *_fs_fstab.fs_spec == '#') continue; if (strunvis(_fs_fstab.fs_spec, _fs_fstab.fs_spec) < 0) goto bad; while ((cp = strsep(&p, " \t\n")) != NULL && *cp == '\0') ; _fs_fstab.fs_file = cp; if (_fs_fstab.fs_file == NULL) goto bad; if (strunvis(_fs_fstab.fs_file, _fs_fstab.fs_file) < 0) goto bad; fixfsfile(); while ((cp = strsep(&p, " \t\n")) != NULL && *cp == '\0') ; _fs_fstab.fs_vfstype = cp; while ((cp = strsep(&p, " \t\n")) != NULL && *cp == '\0') ; _fs_fstab.fs_mntops = cp; if (_fs_fstab.fs_mntops == NULL) goto bad; _fs_fstab.fs_freq = 0; _fs_fstab.fs_passno = 0; while ((cp = strsep(&p, " \t\n")) != NULL && *cp == '\0') ; if (cp != NULL) { _fs_fstab.fs_freq = atoi(cp); while ((cp = strsep(&p, " \t\n")) != NULL && *cp == '\0') ; if (cp != NULL) _fs_fstab.fs_passno = atoi(cp); } (void)strlcpy(subline, _fs_fstab.fs_mntops, sizeof(subline)); p = subline; for (typexx = 0, cp = strsep(&p, ","); cp; cp = strsep(&p, ",")) { if (strlen(cp) != 2) continue; if (!strcmp(cp, FSTAB_RW)) { _fs_fstab.fs_type = FSTAB_RW; break; } if (!strcmp(cp, FSTAB_RQ)) { _fs_fstab.fs_type = FSTAB_RQ; break; } if (!strcmp(cp, FSTAB_RO)) { _fs_fstab.fs_type = FSTAB_RO; break; } if (!strcmp(cp, FSTAB_SW)) { _fs_fstab.fs_type = FSTAB_SW; break; } if (!strcmp(cp, FSTAB_XX)) { _fs_fstab.fs_type = FSTAB_XX; typexx++; break; } } if (typexx) continue; if (cp != NULL) return (1); bad: /* no way to distinguish between EOF and syntax error */ error(EFTYPE); } /* NOTREACHED */ } struct fstab * getfsent(void) { if ((!_fs_fp && !setfsent()) || !fstabscan()) return (NULL); return (&_fs_fstab); } struct fstab * getfsspec(const char *name) { if (setfsent()) while (fstabscan()) if (!strcmp(_fs_fstab.fs_spec, name)) return (&_fs_fstab); return (NULL); } struct fstab * getfsfile(const char *name) { if (setfsent()) while (fstabscan()) if (!strcmp(_fs_fstab.fs_file, name)) return (&_fs_fstab); return (NULL); } int setfsent(void) { if (_fs_fp) { rewind(_fs_fp); LineNo = 0; return (1); } - if (fsp_set == 0) { - if (issetugid()) - setfstab(NULL); - else - setfstab(getenv("PATH_FSTAB")); - } + if (fsp_set == 0) + setfstab(secure_getenv("PATH_FSTAB")); if ((_fs_fp = fopen(path_fstab, "re")) != NULL) { LineNo = 0; return (1); } error(errno); return (0); } void endfsent(void) { if (_fs_fp) { (void)fclose(_fs_fp); _fs_fp = NULL; } fsp_set = 0; } static void error(int err) { char *p; char num[30]; (void)_write(STDERR_FILENO, "fstab: ", 7); (void)_write(STDERR_FILENO, path_fstab, strlen(path_fstab)); (void)_write(STDERR_FILENO, ":", 1); sprintf(num, "%d: ", LineNo); (void)_write(STDERR_FILENO, num, strlen(num)); p = strerror(err); (void)_write(STDERR_FILENO, p, strlen(p)); (void)_write(STDERR_FILENO, "\n", 1); } diff --git a/lib/libc/gen/glob-compat11.c b/lib/libc/gen/glob-compat11.c index 76a4553c922c..26dc9db9ff29 100644 --- a/lib/libc/gen/glob-compat11.c +++ b/lib/libc/gen/glob-compat11.c @@ -1,1091 +1,1090 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Guido van Rossum. * * Copyright (c) 2011 The FreeBSD Foundation * All rights reserved. * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * From: @(#)glob.c 8.3 (Berkeley) 10/13/93 * From: FreeBSD: head/lib/libc/gen/glob.c 317913 2017-05-07 19:52:56Z jilles */ #include __FBSDID("$FreeBSD$"); #include #define _WANT_FREEBSD11_STAT #include #include #define _WANT_FREEBSD11_DIRENT #include #include #include #include #include #include #include #include #include #include #include #include "collate.h" #include "gen-compat.h" #include "glob-compat11.h" /* * glob(3) expansion limits. Stop the expansion if any of these limits * is reached. This caps the runtime in the face of DoS attacks. See * also CVE-2010-2632 */ #define GLOB_LIMIT_BRACE 128 /* number of brace calls */ #define GLOB_LIMIT_PATH 65536 /* number of path elements */ #define GLOB_LIMIT_READDIR 16384 /* number of readdirs */ #define GLOB_LIMIT_STAT 1024 /* number of stat system calls */ #define GLOB_LIMIT_STRING ARG_MAX /* maximum total size for paths */ struct glob_limit { size_t l_brace_cnt; size_t l_path_lim; size_t l_readdir_cnt; size_t l_stat_cnt; size_t l_string_cnt; }; #define DOT L'.' #define EOS L'\0' #define LBRACKET L'[' #define NOT L'!' #define QUESTION L'?' #define QUOTE L'\\' #define RANGE L'-' #define RBRACKET L']' #define SEP L'/' #define STAR L'*' #define TILDE L'~' #define LBRACE L'{' #define RBRACE L'}' #define COMMA L',' #define M_QUOTE 0x8000000000ULL #define M_PROTECT 0x4000000000ULL #define M_MASK 0xffffffffffULL #define M_CHAR 0x00ffffffffULL typedef uint_fast64_t Char; #define CHAR(c) ((Char)((c)&M_CHAR)) #define META(c) ((Char)((c)|M_QUOTE)) #define UNPROT(c) ((c) & ~M_PROTECT) #define M_ALL META(L'*') #define M_END META(L']') #define M_NOT META(L'!') #define M_ONE META(L'?') #define M_RNG META(L'-') #define M_SET META(L'[') #define ismeta(c) (((c)&M_QUOTE) != 0) #ifdef DEBUG #define isprot(c) (((c)&M_PROTECT) != 0) #endif static int compare(const void *, const void *); static int g_Ctoc(const Char *, char *, size_t); static int g_lstat(Char *, struct freebsd11_stat *, glob11_t *); static DIR *g_opendir(Char *, glob11_t *); static const Char *g_strchr(const Char *, wchar_t); #ifdef notdef static Char *g_strcat(Char *, const Char *); #endif static int g_stat(Char *, struct freebsd11_stat *, glob11_t *); static int glob0(const Char *, glob11_t *, struct glob_limit *, const char *); static int glob1(Char *, glob11_t *, struct glob_limit *); static int glob2(Char *, Char *, Char *, Char *, glob11_t *, struct glob_limit *); static int glob3(Char *, Char *, Char *, Char *, Char *, glob11_t *, struct glob_limit *); static int globextend(const Char *, glob11_t *, struct glob_limit *, const char *); static const Char * globtilde(const Char *, Char *, size_t, glob11_t *); static int globexp0(const Char *, glob11_t *, struct glob_limit *, const char *); static int globexp1(const Char *, glob11_t *, struct glob_limit *); static int globexp2(const Char *, const Char *, glob11_t *, struct glob_limit *); static int globfinal(glob11_t *, struct glob_limit *, size_t, const char *); static int match(Char *, Char *, Char *); static int err_nomatch(glob11_t *, struct glob_limit *, const char *); static int err_aborted(glob11_t *, int, char *); #ifdef DEBUG static void qprintf(const char *, Char *); #endif int freebsd11_glob(const char * __restrict pattern, int flags, int (*errfunc)(const char *, int), glob11_t * __restrict pglob) { struct glob_limit limit = { 0, 0, 0, 0, 0 }; const char *patnext; Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot; mbstate_t mbs; wchar_t wc; size_t clen; int too_long; patnext = pattern; if (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; if (!(flags & GLOB_DOOFFS)) pglob->gl_offs = 0; } if (flags & GLOB_LIMIT) { limit.l_path_lim = pglob->gl_matchc; if (limit.l_path_lim == 0) limit.l_path_lim = GLOB_LIMIT_PATH; } pglob->gl_flags = flags & ~GLOB_MAGCHAR; pglob->gl_errfunc = errfunc; pglob->gl_matchc = 0; bufnext = patbuf; bufend = bufnext + MAXPATHLEN - 1; too_long = 1; if (flags & GLOB_NOESCAPE) { memset(&mbs, 0, sizeof(mbs)); while (bufnext <= bufend) { clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) return (err_nomatch(pglob, &limit, pattern)); else if (clen == 0) { too_long = 0; break; } *bufnext++ = wc; patnext += clen; } } else { /* Protect the quoted characters. */ memset(&mbs, 0, sizeof(mbs)); while (bufnext <= bufend) { if (*patnext == '\\') { if (*++patnext == '\0') { *bufnext++ = QUOTE; continue; } prot = M_PROTECT; } else prot = 0; clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) return (err_nomatch(pglob, &limit, pattern)); else if (clen == 0) { too_long = 0; break; } *bufnext++ = wc | prot; patnext += clen; } } if (too_long) return (err_nomatch(pglob, &limit, pattern)); *bufnext = EOS; if (flags & GLOB_BRACE) return (globexp0(patbuf, pglob, &limit, pattern)); else return (glob0(patbuf, pglob, &limit, pattern)); } static int globexp0(const Char *pattern, glob11_t *pglob, struct glob_limit *limit, const char *origpat) { int rv; size_t oldpathc; /* Protect a single {}, for find(1), like csh */ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) { if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { errno = E2BIG; return (GLOB_NOSPACE); } return (glob0(pattern, pglob, limit, origpat)); } oldpathc = pglob->gl_pathc; if ((rv = globexp1(pattern, pglob, limit)) != 0) return rv; return (globfinal(pglob, limit, oldpathc, origpat)); } /* * Expand recursively a glob {} pattern. When there is no more expansion * invoke the standard globbing routine to glob the rest of the magic * characters */ static int globexp1(const Char *pattern, glob11_t *pglob, struct glob_limit *limit) { const Char* ptr; if ((ptr = g_strchr(pattern, LBRACE)) != NULL) { if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { errno = E2BIG; return (GLOB_NOSPACE); } return (globexp2(ptr, pattern, pglob, limit)); } return (glob0(pattern, pglob, limit, NULL)); } /* * Recursive brace globbing helper. Tries to expand a single brace. * If it succeeds then it invokes globexp1 with the new pattern. * If it fails then it tries to glob the rest of the pattern and returns. */ static int globexp2(const Char *ptr, const Char *pattern, glob11_t *pglob, struct glob_limit *limit) { int i, rv; Char *lm, *ls; const Char *pe, *pm, *pm1, *pl; Char patbuf[MAXPATHLEN]; /* copy part up to the brace */ for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) continue; *lm = EOS; ls = lm; /* Find the balanced brace */ for (i = 0, pe = ++ptr; *pe != EOS; pe++) if (*pe == LBRACKET) { /* Ignore everything between [] */ for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) continue; if (*pe == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE */ pe = pm; } } else if (*pe == LBRACE) i++; else if (*pe == RBRACE) { if (i == 0) break; i--; } /* Non matching braces; just glob the pattern */ if (i != 0 || *pe == EOS) return (glob0(pattern, pglob, limit, NULL)); for (i = 0, pl = pm = ptr; pm <= pe; pm++) switch (*pm) { case LBRACKET: /* Ignore everything between [] */ for (pm1 = pm++; *pm != RBRACKET && *pm != EOS; pm++) continue; if (*pm == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE */ pm = pm1; } break; case LBRACE: i++; break; case RBRACE: if (i) { i--; break; } /* FALLTHROUGH */ case COMMA: if (i && *pm == COMMA) break; else { /* Append the current string */ for (lm = ls; (pl < pm); *lm++ = *pl++) continue; /* * Append the rest of the pattern after the * closing brace */ for (pl = pe + 1; (*lm++ = *pl++) != EOS;) continue; /* Expand the current pattern */ #ifdef DEBUG qprintf("globexp2:", patbuf); #endif rv = globexp1(patbuf, pglob, limit); if (rv) return (rv); /* move after the comma, to the next string */ pl = pm + 1; } break; default: break; } return (0); } /* * expand tilde from the passwd file. */ static const Char * globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob11_t *pglob) { struct passwd *pwd; char *h, *sc; const Char *p; Char *b, *eb; wchar_t wc; wchar_t wbuf[MAXPATHLEN]; wchar_t *wbufend, *dc; size_t clen; mbstate_t mbs; int too_long; if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) return (pattern); /* * Copy up to the end of the string or / */ eb = &patbuf[patbuf_len - 1]; for (p = pattern + 1, b = patbuf; b < eb && *p != EOS && UNPROT(*p) != SEP; *b++ = *p++) continue; if (*p != EOS && UNPROT(*p) != SEP) return (NULL); *b = EOS; h = NULL; if (patbuf[0] == EOS) { /* * handle a plain ~ or ~/ by expanding $HOME first (iff * we're not running setuid or setgid) and then trying * the password file */ - if (issetugid() != 0 || - (h = getenv("HOME")) == NULL) { + if ((h = secure_getenv("HOME")) == NULL) { if (((h = getlogin()) != NULL && (pwd = getpwnam(h)) != NULL) || (pwd = getpwuid(getuid())) != NULL) h = pwd->pw_dir; else return (pattern); } } else { /* * Expand a ~user */ if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf))) return (NULL); if ((pwd = getpwnam((char *)wbuf)) == NULL) return (pattern); else h = pwd->pw_dir; } /* Copy the home directory */ dc = wbuf; sc = h; wbufend = wbuf + MAXPATHLEN - 1; too_long = 1; memset(&mbs, 0, sizeof(mbs)); while (dc <= wbufend) { clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) { /* XXX See initial comment #2. */ wc = (unsigned char)*sc; clen = 1; memset(&mbs, 0, sizeof(mbs)); } if ((*dc++ = wc) == EOS) { too_long = 0; break; } sc += clen; } if (too_long) return (NULL); dc = wbuf; for (b = patbuf; b < eb && *dc != EOS; *b++ = *dc++ | M_PROTECT) continue; if (*dc != EOS) return (NULL); /* Append the rest of the pattern */ if (*p != EOS) { too_long = 1; while (b <= eb) { if ((*b++ = *p++) == EOS) { too_long = 0; break; } } if (too_long) return (NULL); } else *b = EOS; return (patbuf); } /* * The main glob() routine: compiles the pattern (optionally processing * quotes), calls glob1() to do the real pattern matching, and finally * sorts the list (unless unsorted operation is requested). Returns 0 * if things went well, nonzero if errors occurred. */ static int glob0(const Char *pattern, glob11_t *pglob, struct glob_limit *limit, const char *origpat) { const Char *qpatnext; int err; size_t oldpathc; Char *bufnext, c, patbuf[MAXPATHLEN]; qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); if (qpatnext == NULL) { errno = E2BIG; return (GLOB_NOSPACE); } oldpathc = pglob->gl_pathc; bufnext = patbuf; /* We don't need to check for buffer overflow any more. */ while ((c = *qpatnext++) != EOS) { switch (c) { case LBRACKET: c = *qpatnext; if (c == NOT) ++qpatnext; if (*qpatnext == EOS || g_strchr(qpatnext+1, RBRACKET) == NULL) { *bufnext++ = LBRACKET; if (c == NOT) --qpatnext; break; } *bufnext++ = M_SET; if (c == NOT) *bufnext++ = M_NOT; c = *qpatnext++; do { *bufnext++ = CHAR(c); if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET) { *bufnext++ = M_RNG; *bufnext++ = CHAR(c); qpatnext += 2; } } while ((c = *qpatnext++) != RBRACKET); pglob->gl_flags |= GLOB_MAGCHAR; *bufnext++ = M_END; break; case QUESTION: pglob->gl_flags |= GLOB_MAGCHAR; *bufnext++ = M_ONE; break; case STAR: pglob->gl_flags |= GLOB_MAGCHAR; /* collapse adjacent stars to one, * to avoid exponential behavior */ if (bufnext == patbuf || bufnext[-1] != M_ALL) *bufnext++ = M_ALL; break; default: *bufnext++ = CHAR(c); break; } } *bufnext = EOS; #ifdef DEBUG qprintf("glob0:", patbuf); #endif if ((err = glob1(patbuf, pglob, limit)) != 0) return(err); if (origpat != NULL) return (globfinal(pglob, limit, oldpathc, origpat)); return (0); } static int globfinal(glob11_t *pglob, struct glob_limit *limit, size_t oldpathc, const char *origpat) { if (pglob->gl_pathc == oldpathc) return (err_nomatch(pglob, limit, origpat)); if (!(pglob->gl_flags & GLOB_NOSORT)) qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, pglob->gl_pathc - oldpathc, sizeof(char *), compare); return (0); } static int compare(const void *p, const void *q) { return (strcoll(*(char **)p, *(char **)q)); } static int glob1(Char *pattern, glob11_t *pglob, struct glob_limit *limit) { Char pathbuf[MAXPATHLEN]; /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ if (*pattern == EOS) return (0); return (glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1, pattern, pglob, limit)); } /* * The functions glob2 and glob3 are mutually recursive; there is one level * of recursion for each segment in the pattern that contains one or more * meta characters. */ static int glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, glob11_t *pglob, struct glob_limit *limit) { struct freebsd11_stat sb; Char *p, *q; int anymeta; /* * Loop over pattern segments until end of pattern or until * segment with meta character found. */ for (anymeta = 0;;) { if (*pattern == EOS) { /* End of pattern? */ *pathend = EOS; if (g_lstat(pathbuf, &sb, pglob)) return (0); if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_stat_cnt++ >= GLOB_LIMIT_STAT) { errno = E2BIG; return (GLOB_NOSPACE); } if ((pglob->gl_flags & GLOB_MARK) && UNPROT(pathend[-1]) != SEP && (S_ISDIR(sb.st_mode) || (S_ISLNK(sb.st_mode) && g_stat(pathbuf, &sb, pglob) == 0 && S_ISDIR(sb.st_mode)))) { if (pathend + 1 > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *pathend++ = SEP; *pathend = EOS; } ++pglob->gl_matchc; return (globextend(pathbuf, pglob, limit, NULL)); } /* Find end of next segment, copy tentatively to pathend. */ q = pathend; p = pattern; while (*p != EOS && UNPROT(*p) != SEP) { if (ismeta(*p)) anymeta = 1; if (q + 1 > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *q++ = *p++; } if (!anymeta) { /* No expansion, do next segment. */ pathend = q; pattern = p; while (UNPROT(*pattern) == SEP) { if (pathend + 1 > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *pathend++ = *pattern++; } } else /* Need expansion, recurse. */ return (glob3(pathbuf, pathend, pathend_last, pattern, p, pglob, limit)); } /* NOTREACHED */ } static int glob3(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, Char *restpattern, glob11_t *pglob, struct glob_limit *limit) { struct freebsd11_dirent *dp; DIR *dirp; int err, too_long, saverrno, saverrno2; char buf[MAXPATHLEN + MB_LEN_MAX - 1]; struct freebsd11_dirent *(*readdirfunc)(DIR *); if (pathend > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *pathend = EOS; if (pglob->gl_errfunc != NULL && g_Ctoc(pathbuf, buf, sizeof(buf))) { errno = E2BIG; return (GLOB_NOSPACE); } saverrno = errno; errno = 0; if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { if (errno == ENOENT || errno == ENOTDIR) return (0); err = err_aborted(pglob, errno, buf); if (errno == 0) errno = saverrno; return (err); } err = 0; /* pglob->gl_readdir takes a void *, fix this manually */ if (pglob->gl_flags & GLOB_ALTDIRFUNC) readdirfunc = (struct freebsd11_dirent *(*)(DIR *))pglob->gl_readdir; else readdirfunc = freebsd11_readdir; errno = 0; /* Search directory for matching names. */ while ((dp = (*readdirfunc)(dirp)) != NULL) { char *sc; Char *dc; wchar_t wc; size_t clen; mbstate_t mbs; if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) { errno = E2BIG; err = GLOB_NOSPACE; break; } /* Initial DOT must be matched literally. */ if (dp->d_name[0] == '.' && UNPROT(*pattern) != DOT) { errno = 0; continue; } memset(&mbs, 0, sizeof(mbs)); dc = pathend; sc = dp->d_name; too_long = 1; while (dc <= pathend_last) { clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) { /* XXX See initial comment #2. */ wc = (unsigned char)*sc; clen = 1; memset(&mbs, 0, sizeof(mbs)); } if ((*dc++ = wc) == EOS) { too_long = 0; break; } sc += clen; } if (too_long && (err = err_aborted(pglob, ENAMETOOLONG, buf))) { errno = ENAMETOOLONG; break; } if (too_long || !match(pathend, pattern, restpattern)) { *pathend = EOS; errno = 0; continue; } if (errno == 0) errno = saverrno; err = glob2(pathbuf, --dc, pathend_last, restpattern, pglob, limit); if (err) break; errno = 0; } saverrno2 = errno; if (pglob->gl_flags & GLOB_ALTDIRFUNC) (*pglob->gl_closedir)(dirp); else closedir(dirp); errno = saverrno2; if (err) return (err); if (dp == NULL && errno != 0 && (err = err_aborted(pglob, errno, buf))) return (err); if (errno == 0) errno = saverrno; return (0); } /* * Extend the gl_pathv member of a glob11_t structure to accommodate a new item, * add the new item, and update gl_pathc. * * This assumes the BSD realloc, which only copies the block when its size * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic * behavior. * * Return 0 if new item added, error code if memory couldn't be allocated. * * Invariant of the glob11_t structure: * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int globextend(const Char *path, glob11_t *pglob, struct glob_limit *limit, const char *origpat) { char **pathv; size_t i, newn, len; char *copy; const Char *p; if ((pglob->gl_flags & GLOB_LIMIT) && pglob->gl_matchc > limit->l_path_lim) { errno = E2BIG; return (GLOB_NOSPACE); } newn = 2 + pglob->gl_pathc + pglob->gl_offs; /* reallocarray(NULL, newn, size) is equivalent to malloc(newn*size). */ pathv = reallocarray(pglob->gl_pathv, newn, sizeof(*pathv)); if (pathv == NULL) return (GLOB_NOSPACE); if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { /* first time around -- clear initial gl_offs items */ pathv += pglob->gl_offs; for (i = pglob->gl_offs + 1; --i > 0; ) *--pathv = NULL; } pglob->gl_pathv = pathv; if (origpat != NULL) copy = strdup(origpat); else { for (p = path; *p++ != EOS;) continue; len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ if ((copy = malloc(len)) != NULL) { if (g_Ctoc(path, copy, len)) { free(copy); errno = E2BIG; return (GLOB_NOSPACE); } } } if (copy != NULL) { limit->l_string_cnt += strlen(copy) + 1; if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_string_cnt >= GLOB_LIMIT_STRING) { free(copy); errno = E2BIG; return (GLOB_NOSPACE); } pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; } pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; return (copy == NULL ? GLOB_NOSPACE : 0); } /* * pattern matching function for filenames. */ static int match(Char *name, Char *pat, Char *patend) { int ok, negate_range; Char c, k, *nextp, *nextn; struct xlocale_collate *table = (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; nextn = NULL; nextp = NULL; while (1) { while (pat < patend) { c = *pat++; switch (c & M_MASK) { case M_ALL: if (pat == patend) return (1); if (*name == EOS) return (0); nextn = name + 1; nextp = pat - 1; break; case M_ONE: if (*name++ == EOS) goto fail; break; case M_SET: ok = 0; if ((k = *name++) == EOS) goto fail; negate_range = ((*pat & M_MASK) == M_NOT); if (negate_range != 0) ++pat; while (((c = *pat++) & M_MASK) != M_END) if ((*pat & M_MASK) == M_RNG) { if (table->__collate_load_error ? CHAR(c) <= CHAR(k) && CHAR(k) <= CHAR(pat[1]) : __wcollate_range_cmp(CHAR(c), CHAR(k)) <= 0 && __wcollate_range_cmp(CHAR(k), CHAR(pat[1])) <= 0) ok = 1; pat += 2; } else if (c == k) ok = 1; if (ok == negate_range) goto fail; break; default: if (*name++ != c) goto fail; break; } } if (*name == EOS) return (1); fail: if (nextn == NULL) break; pat = nextp; name = nextn; } return (0); } /* Free allocated data belonging to a glob11_t structure. */ void freebsd11_globfree(glob11_t *pglob) { size_t i; char **pp; if (pglob->gl_pathv != NULL) { pp = pglob->gl_pathv + pglob->gl_offs; for (i = pglob->gl_pathc; i--; ++pp) if (*pp) free(*pp); free(pglob->gl_pathv); pglob->gl_pathv = NULL; } } static DIR * g_opendir(Char *str, glob11_t *pglob) { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (*str == EOS) strcpy(buf, "."); else { if (g_Ctoc(str, buf, sizeof(buf))) { errno = ENAMETOOLONG; return (NULL); } } if (pglob->gl_flags & GLOB_ALTDIRFUNC) return ((*pglob->gl_opendir)(buf)); return (opendir(buf)); } static int g_lstat(Char *fn, struct freebsd11_stat *sb, glob11_t *pglob) { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (g_Ctoc(fn, buf, sizeof(buf))) { errno = ENAMETOOLONG; return (-1); } if (pglob->gl_flags & GLOB_ALTDIRFUNC) return((*pglob->gl_lstat)(buf, sb)); return (freebsd11_lstat(buf, sb)); } static int g_stat(Char *fn, struct freebsd11_stat *sb, glob11_t *pglob) { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (g_Ctoc(fn, buf, sizeof(buf))) { errno = ENAMETOOLONG; return (-1); } if (pglob->gl_flags & GLOB_ALTDIRFUNC) return ((*pglob->gl_stat)(buf, sb)); return (freebsd11_stat(buf, sb)); } static const Char * g_strchr(const Char *str, wchar_t ch) { do { if (*str == ch) return (str); } while (*str++); return (NULL); } static int g_Ctoc(const Char *str, char *buf, size_t len) { mbstate_t mbs; size_t clen; memset(&mbs, 0, sizeof(mbs)); while (len >= MB_CUR_MAX) { clen = wcrtomb(buf, CHAR(*str), &mbs); if (clen == (size_t)-1) { /* XXX See initial comment #2. */ *buf = (char)CHAR(*str); clen = 1; memset(&mbs, 0, sizeof(mbs)); } if (CHAR(*str) == EOS) return (0); str++; buf += clen; len -= clen; } return (1); } static int err_nomatch(glob11_t *pglob, struct glob_limit *limit, const char *origpat) { /* * If there was no match we are going to append the origpat * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified * and the origpat did not contain any magic characters * GLOB_NOMAGIC is there just for compatibility with csh. */ if ((pglob->gl_flags & GLOB_NOCHECK) || ((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR))) return (globextend(NULL, pglob, limit, origpat)); return (GLOB_NOMATCH); } static int err_aborted(glob11_t *pglob, int err, char *buf) { if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) || (pglob->gl_flags & GLOB_ERR)) return (GLOB_ABORTED); return (0); } #ifdef DEBUG static void qprintf(const char *str, Char *s) { Char *p; (void)printf("%s\n", str); if (s != NULL) { for (p = s; *p != EOS; p++) (void)printf("%c", (char)CHAR(*p)); (void)printf("\n"); for (p = s; *p != EOS; p++) (void)printf("%c", (isprot(*p) ? '\\' : ' ')); (void)printf("\n"); for (p = s; *p != EOS; p++) (void)printf("%c", (ismeta(*p) ? '_' : ' ')); (void)printf("\n"); } } #endif __sym_compat(glob, freebsd11_glob, FBSD_1.0); __sym_compat(globfree, freebsd11_globfree, FBSD_1.0); diff --git a/lib/libc/gen/glob.c b/lib/libc/gen/glob.c index 2e8bf6310641..43dd77df8119 100644 --- a/lib/libc/gen/glob.c +++ b/lib/libc/gen/glob.c @@ -1,1119 +1,1118 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Guido van Rossum. * * Copyright (c) 2011 The FreeBSD Foundation * * Portions of this software were developed by David Chisnall * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __SCCSID("@(#)glob.c 8.3 (Berkeley) 10/13/93"); __FBSDID("$FreeBSD$"); /* * glob(3) -- a superset of the one defined in POSIX 1003.2. * * The [!...] convention to negate a range is supported (SysV, Posix, ksh). * * Optional extra services, controlled by flags not defined by POSIX: * * GLOB_QUOTE: * Escaping convention: \ inhibits any special meaning the following * character might have (except \ at end of string is retained). * GLOB_MAGCHAR: * Set in gl_flags if pattern contained a globbing character. * GLOB_NOMAGIC: * Same as GLOB_NOCHECK, but it will only append pattern if it did * not contain any magic characters. [Used in csh style globbing] * GLOB_ALTDIRFUNC: * Use alternately specified directory access functions. * GLOB_TILDE: * expand ~user/foo to the /home/dir/of/user/foo * GLOB_BRACE: * expand {1,2}{a,b} to 1a 1b 2a 2b * gl_matchc: * Number of matches in the current invocation of glob. */ /* * Some notes on multibyte character support: * 1. Patterns with illegal byte sequences match nothing - even if * GLOB_NOCHECK is specified. * 2. Illegal byte sequences in filenames are handled by treating them as * single-byte characters with a values of such bytes of the sequence * cast to wchar_t. * 3. State-dependent encodings are not currently supported. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "collate.h" /* * glob(3) expansion limits. Stop the expansion if any of these limits * is reached. This caps the runtime in the face of DoS attacks. See * also CVE-2010-2632 */ #define GLOB_LIMIT_BRACE 128 /* number of brace calls */ #define GLOB_LIMIT_PATH 65536 /* number of path elements */ #define GLOB_LIMIT_READDIR 16384 /* number of readdirs */ #define GLOB_LIMIT_STAT 1024 /* number of stat system calls */ #define GLOB_LIMIT_STRING ARG_MAX /* maximum total size for paths */ struct glob_limit { size_t l_brace_cnt; size_t l_path_lim; size_t l_readdir_cnt; size_t l_stat_cnt; size_t l_string_cnt; }; #define DOT L'.' #define EOS L'\0' #define LBRACKET L'[' #define NOT L'!' #define QUESTION L'?' #define QUOTE L'\\' #define RANGE L'-' #define RBRACKET L']' #define SEP L'/' #define STAR L'*' #define TILDE L'~' #define LBRACE L'{' #define RBRACE L'}' #define COMMA L',' #define M_QUOTE 0x8000000000ULL #define M_PROTECT 0x4000000000ULL #define M_MASK 0xffffffffffULL #define M_CHAR 0x00ffffffffULL typedef uint_fast64_t Char; #define CHAR(c) ((Char)((c)&M_CHAR)) #define META(c) ((Char)((c)|M_QUOTE)) #define UNPROT(c) ((c) & ~M_PROTECT) #define M_ALL META(L'*') #define M_END META(L']') #define M_NOT META(L'!') #define M_ONE META(L'?') #define M_RNG META(L'-') #define M_SET META(L'[') #define ismeta(c) (((c)&M_QUOTE) != 0) #ifdef DEBUG #define isprot(c) (((c)&M_PROTECT) != 0) #endif static int compare(const void *, const void *); static int g_Ctoc(const Char *, char *, size_t); static int g_lstat(Char *, struct stat *, glob_t *); static DIR *g_opendir(Char *, glob_t *); static const Char *g_strchr(const Char *, wchar_t); #ifdef notdef static Char *g_strcat(Char *, const Char *); #endif static int g_stat(Char *, struct stat *, glob_t *); static int glob0(const Char *, glob_t *, struct glob_limit *, const char *); static int glob1(Char *, glob_t *, struct glob_limit *); static int glob2(Char *, Char *, Char *, Char *, glob_t *, struct glob_limit *); static int glob3(Char *, Char *, Char *, Char *, Char *, glob_t *, struct glob_limit *); static int globextend(const Char *, glob_t *, struct glob_limit *, const char *); static const Char * globtilde(const Char *, Char *, size_t, glob_t *); static int globexp0(const Char *, glob_t *, struct glob_limit *, const char *); static int globexp1(const Char *, glob_t *, struct glob_limit *); static int globexp2(const Char *, const Char *, glob_t *, struct glob_limit *); static int globfinal(glob_t *, struct glob_limit *, size_t, const char *); static int match(Char *, Char *, Char *); static int err_nomatch(glob_t *, struct glob_limit *, const char *); static int err_aborted(glob_t *, int, char *); #ifdef DEBUG static void qprintf(const char *, Char *); #endif int glob(const char * __restrict pattern, int flags, int (*errfunc)(const char *, int), glob_t * __restrict pglob) { struct glob_limit limit = { 0, 0, 0, 0, 0 }; const char *patnext; Char *bufnext, *bufend, patbuf[MAXPATHLEN], prot; mbstate_t mbs; wchar_t wc; size_t clen; int too_long; patnext = pattern; if (!(flags & GLOB_APPEND)) { pglob->gl_pathc = 0; pglob->gl_pathv = NULL; if (!(flags & GLOB_DOOFFS)) pglob->gl_offs = 0; } if (flags & GLOB_LIMIT) { limit.l_path_lim = pglob->gl_matchc; if (limit.l_path_lim == 0) limit.l_path_lim = GLOB_LIMIT_PATH; } pglob->gl_flags = flags & ~GLOB_MAGCHAR; pglob->gl_errfunc = errfunc; pglob->gl_matchc = 0; bufnext = patbuf; bufend = bufnext + MAXPATHLEN - 1; too_long = 1; if (flags & GLOB_NOESCAPE) { memset(&mbs, 0, sizeof(mbs)); while (bufnext <= bufend) { clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) return (err_nomatch(pglob, &limit, pattern)); else if (clen == 0) { too_long = 0; break; } *bufnext++ = wc; patnext += clen; } } else { /* Protect the quoted characters. */ memset(&mbs, 0, sizeof(mbs)); while (bufnext <= bufend) { if (*patnext == '\\') { if (*++patnext == '\0') { *bufnext++ = QUOTE; continue; } prot = M_PROTECT; } else prot = 0; clen = mbrtowc(&wc, patnext, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) return (err_nomatch(pglob, &limit, pattern)); else if (clen == 0) { too_long = 0; break; } *bufnext++ = wc | prot; patnext += clen; } } if (too_long) return (err_nomatch(pglob, &limit, pattern)); *bufnext = EOS; if (flags & GLOB_BRACE) return (globexp0(patbuf, pglob, &limit, pattern)); else return (glob0(patbuf, pglob, &limit, pattern)); } static int globexp0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, const char *origpat) { int rv; size_t oldpathc; /* Protect a single {}, for find(1), like csh */ if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS) { if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { errno = E2BIG; return (GLOB_NOSPACE); } return (glob0(pattern, pglob, limit, origpat)); } oldpathc = pglob->gl_pathc; if ((rv = globexp1(pattern, pglob, limit)) != 0) return rv; return (globfinal(pglob, limit, oldpathc, origpat)); } /* * Expand recursively a glob {} pattern. When there is no more expansion * invoke the standard globbing routine to glob the rest of the magic * characters */ static int globexp1(const Char *pattern, glob_t *pglob, struct glob_limit *limit) { const Char* ptr; if ((ptr = g_strchr(pattern, LBRACE)) != NULL) { if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_brace_cnt++ >= GLOB_LIMIT_BRACE) { errno = E2BIG; return (GLOB_NOSPACE); } return (globexp2(ptr, pattern, pglob, limit)); } return (glob0(pattern, pglob, limit, NULL)); } /* * Recursive brace globbing helper. Tries to expand a single brace. * If it succeeds then it invokes globexp1 with the new pattern. * If it fails then it tries to glob the rest of the pattern and returns. */ static int globexp2(const Char *ptr, const Char *pattern, glob_t *pglob, struct glob_limit *limit) { int i, rv; Char *lm, *ls; const Char *pe, *pm, *pm1, *pl; Char patbuf[MAXPATHLEN]; /* copy part up to the brace */ for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++) continue; *lm = EOS; ls = lm; /* Find the balanced brace */ for (i = 0, pe = ++ptr; *pe != EOS; pe++) if (*pe == LBRACKET) { /* Ignore everything between [] */ for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++) continue; if (*pe == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE */ pe = pm; } } else if (*pe == LBRACE) i++; else if (*pe == RBRACE) { if (i == 0) break; i--; } /* Non matching braces; just glob the pattern */ if (i != 0 || *pe == EOS) return (glob0(pattern, pglob, limit, NULL)); for (i = 0, pl = pm = ptr; pm <= pe; pm++) switch (*pm) { case LBRACKET: /* Ignore everything between [] */ for (pm1 = pm++; *pm != RBRACKET && *pm != EOS; pm++) continue; if (*pm == EOS) { /* * We could not find a matching RBRACKET. * Ignore and just look for RBRACE */ pm = pm1; } break; case LBRACE: i++; break; case RBRACE: if (i) { i--; break; } /* FALLTHROUGH */ case COMMA: if (i && *pm == COMMA) break; else { /* Append the current string */ for (lm = ls; (pl < pm); *lm++ = *pl++) continue; /* * Append the rest of the pattern after the * closing brace */ for (pl = pe + 1; (*lm++ = *pl++) != EOS;) continue; /* Expand the current pattern */ #ifdef DEBUG qprintf("globexp2:", patbuf); #endif rv = globexp1(patbuf, pglob, limit); if (rv) return (rv); /* move after the comma, to the next string */ pl = pm + 1; } break; default: break; } return (0); } /* * expand tilde from the passwd file. */ static const Char * globtilde(const Char *pattern, Char *patbuf, size_t patbuf_len, glob_t *pglob) { struct passwd *pwd; char *h, *sc; const Char *p; Char *b, *eb; wchar_t wc; wchar_t wbuf[MAXPATHLEN]; wchar_t *wbufend, *dc; size_t clen; mbstate_t mbs; int too_long; if (*pattern != TILDE || !(pglob->gl_flags & GLOB_TILDE)) return (pattern); /* * Copy up to the end of the string or / */ eb = &patbuf[patbuf_len - 1]; for (p = pattern + 1, b = patbuf; b < eb && *p != EOS && UNPROT(*p) != SEP; *b++ = *p++) continue; if (*p != EOS && UNPROT(*p) != SEP) return (NULL); *b = EOS; h = NULL; if (patbuf[0] == EOS) { /* * handle a plain ~ or ~/ by expanding $HOME first (iff * we're not running setuid or setgid) and then trying * the password file */ - if (issetugid() != 0 || - (h = getenv("HOME")) == NULL) { + if ((h = secure_getenv("HOME")) == NULL) { if (((h = getlogin()) != NULL && (pwd = getpwnam(h)) != NULL) || (pwd = getpwuid(getuid())) != NULL) h = pwd->pw_dir; else return (pattern); } } else { /* * Expand a ~user */ if (g_Ctoc(patbuf, (char *)wbuf, sizeof(wbuf))) return (NULL); if ((pwd = getpwnam((char *)wbuf)) == NULL) return (pattern); else h = pwd->pw_dir; } /* Copy the home directory */ dc = wbuf; sc = h; wbufend = wbuf + MAXPATHLEN - 1; too_long = 1; memset(&mbs, 0, sizeof(mbs)); while (dc <= wbufend) { clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) { /* XXX See initial comment #2. */ wc = (unsigned char)*sc; clen = 1; memset(&mbs, 0, sizeof(mbs)); } if ((*dc++ = wc) == EOS) { too_long = 0; break; } sc += clen; } if (too_long) return (NULL); dc = wbuf; for (b = patbuf; b < eb && *dc != EOS; *b++ = *dc++ | M_PROTECT) continue; if (*dc != EOS) return (NULL); /* Append the rest of the pattern */ if (*p != EOS) { too_long = 1; while (b <= eb) { if ((*b++ = *p++) == EOS) { too_long = 0; break; } } if (too_long) return (NULL); } else *b = EOS; return (patbuf); } /* * The main glob() routine: compiles the pattern (optionally processing * quotes), calls glob1() to do the real pattern matching, and finally * sorts the list (unless unsorted operation is requested). Returns 0 * if things went well, nonzero if errors occurred. */ static int glob0(const Char *pattern, glob_t *pglob, struct glob_limit *limit, const char *origpat) { const Char *qpatnext; int err; size_t oldpathc; Char *bufnext, c, patbuf[MAXPATHLEN]; qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob); if (qpatnext == NULL) { errno = E2BIG; return (GLOB_NOSPACE); } oldpathc = pglob->gl_pathc; bufnext = patbuf; /* We don't need to check for buffer overflow any more. */ while ((c = *qpatnext++) != EOS) { switch (c) { case LBRACKET: c = *qpatnext; if (c == NOT) ++qpatnext; if (*qpatnext == EOS || g_strchr(qpatnext+1, RBRACKET) == NULL) { *bufnext++ = LBRACKET; if (c == NOT) --qpatnext; break; } *bufnext++ = M_SET; if (c == NOT) *bufnext++ = M_NOT; c = *qpatnext++; do { *bufnext++ = CHAR(c); if (*qpatnext == RANGE && (c = qpatnext[1]) != RBRACKET) { *bufnext++ = M_RNG; *bufnext++ = CHAR(c); qpatnext += 2; } } while ((c = *qpatnext++) != RBRACKET); pglob->gl_flags |= GLOB_MAGCHAR; *bufnext++ = M_END; break; case QUESTION: pglob->gl_flags |= GLOB_MAGCHAR; *bufnext++ = M_ONE; break; case STAR: pglob->gl_flags |= GLOB_MAGCHAR; /* collapse adjacent stars to one, * to ensure "**" at the end continues to match the * empty string */ if (bufnext == patbuf || bufnext[-1] != M_ALL) *bufnext++ = M_ALL; break; default: *bufnext++ = CHAR(c); break; } } *bufnext = EOS; #ifdef DEBUG qprintf("glob0:", patbuf); #endif if ((err = glob1(patbuf, pglob, limit)) != 0) return(err); if (origpat != NULL) return (globfinal(pglob, limit, oldpathc, origpat)); return (0); } static int globfinal(glob_t *pglob, struct glob_limit *limit, size_t oldpathc, const char *origpat) { if (pglob->gl_pathc == oldpathc) return (err_nomatch(pglob, limit, origpat)); if (!(pglob->gl_flags & GLOB_NOSORT)) qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc, pglob->gl_pathc - oldpathc, sizeof(char *), compare); return (0); } static int compare(const void *p, const void *q) { return (strcoll(*(char **)p, *(char **)q)); } static int glob1(Char *pattern, glob_t *pglob, struct glob_limit *limit) { Char pathbuf[MAXPATHLEN]; /* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */ if (*pattern == EOS) return (0); return (glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1, pattern, pglob, limit)); } /* * The functions glob2 and glob3 are mutually recursive; there is one level * of recursion for each segment in the pattern that contains one or more * meta characters. */ static int glob2(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, glob_t *pglob, struct glob_limit *limit) { struct stat sb; Char *p, *q; int anymeta; /* * Loop over pattern segments until end of pattern or until * segment with meta character found. */ for (anymeta = 0;;) { if (*pattern == EOS) { /* End of pattern? */ *pathend = EOS; if (g_lstat(pathbuf, &sb, pglob)) return (0); if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_stat_cnt++ >= GLOB_LIMIT_STAT) { errno = E2BIG; return (GLOB_NOSPACE); } if ((pglob->gl_flags & GLOB_MARK) && UNPROT(pathend[-1]) != SEP && (S_ISDIR(sb.st_mode) || (S_ISLNK(sb.st_mode) && g_stat(pathbuf, &sb, pglob) == 0 && S_ISDIR(sb.st_mode)))) { if (pathend + 1 > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *pathend++ = SEP; *pathend = EOS; } ++pglob->gl_matchc; return (globextend(pathbuf, pglob, limit, NULL)); } /* Find end of next segment, copy tentatively to pathend. */ q = pathend; p = pattern; while (*p != EOS && UNPROT(*p) != SEP) { if (ismeta(*p)) anymeta = 1; if (q + 1 > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *q++ = *p++; } if (!anymeta) { /* No expansion, do next segment. */ pathend = q; pattern = p; while (UNPROT(*pattern) == SEP) { if (pathend + 1 > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *pathend++ = *pattern++; } } else /* Need expansion, recurse. */ return (glob3(pathbuf, pathend, pathend_last, pattern, p, pglob, limit)); } /* NOTREACHED */ } static int glob3(Char *pathbuf, Char *pathend, Char *pathend_last, Char *pattern, Char *restpattern, glob_t *pglob, struct glob_limit *limit) { struct dirent *dp; DIR *dirp; int err, too_long, saverrno, saverrno2; char buf[MAXPATHLEN + MB_LEN_MAX - 1]; struct dirent *(*readdirfunc)(DIR *); if (pathend > pathend_last) { errno = E2BIG; return (GLOB_NOSPACE); } *pathend = EOS; if (pglob->gl_errfunc != NULL && g_Ctoc(pathbuf, buf, sizeof(buf))) { errno = E2BIG; return (GLOB_NOSPACE); } saverrno = errno; errno = 0; if ((dirp = g_opendir(pathbuf, pglob)) == NULL) { if (errno == ENOENT || errno == ENOTDIR) return (0); err = err_aborted(pglob, errno, buf); if (errno == 0) errno = saverrno; return (err); } err = 0; /* pglob->gl_readdir takes a void *, fix this manually */ if (pglob->gl_flags & GLOB_ALTDIRFUNC) readdirfunc = (struct dirent *(*)(DIR *))pglob->gl_readdir; else readdirfunc = readdir; errno = 0; /* Search directory for matching names. */ while ((dp = (*readdirfunc)(dirp)) != NULL) { char *sc; Char *dc; wchar_t wc; size_t clen; mbstate_t mbs; if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_readdir_cnt++ >= GLOB_LIMIT_READDIR) { errno = E2BIG; err = GLOB_NOSPACE; break; } /* Initial DOT must be matched literally. */ if (dp->d_name[0] == '.' && UNPROT(*pattern) != DOT) { errno = 0; continue; } memset(&mbs, 0, sizeof(mbs)); dc = pathend; sc = dp->d_name; too_long = 1; while (dc <= pathend_last) { clen = mbrtowc(&wc, sc, MB_LEN_MAX, &mbs); if (clen == (size_t)-1 || clen == (size_t)-2) { /* XXX See initial comment #2. */ wc = (unsigned char)*sc; clen = 1; memset(&mbs, 0, sizeof(mbs)); } if ((*dc++ = wc) == EOS) { too_long = 0; break; } sc += clen; } if (too_long && (err = err_aborted(pglob, ENAMETOOLONG, buf))) { errno = ENAMETOOLONG; break; } if (too_long || !match(pathend, pattern, restpattern)) { *pathend = EOS; errno = 0; continue; } if (errno == 0) errno = saverrno; err = glob2(pathbuf, --dc, pathend_last, restpattern, pglob, limit); if (err) break; errno = 0; } saverrno2 = errno; if (pglob->gl_flags & GLOB_ALTDIRFUNC) (*pglob->gl_closedir)(dirp); else closedir(dirp); errno = saverrno2; if (err) return (err); if (dp == NULL && errno != 0 && (err = err_aborted(pglob, errno, buf))) return (err); if (errno == 0) errno = saverrno; return (0); } /* * Extend the gl_pathv member of a glob_t structure to accommodate a new item, * add the new item, and update gl_pathc. * * This assumes the BSD realloc, which only copies the block when its size * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic * behavior. * * Return 0 if new item added, error code if memory couldn't be allocated. * * Invariant of the glob_t structure: * Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and * gl_pathv points to (gl_offs + gl_pathc + 1) items. */ static int globextend(const Char *path, glob_t *pglob, struct glob_limit *limit, const char *origpat) { char **pathv; size_t i, newn, len; char *copy; const Char *p; if ((pglob->gl_flags & GLOB_LIMIT) && pglob->gl_matchc > limit->l_path_lim) { errno = E2BIG; return (GLOB_NOSPACE); } newn = 2 + pglob->gl_pathc + pglob->gl_offs; /* reallocarray(NULL, newn, size) is equivalent to malloc(newn*size). */ pathv = reallocarray(pglob->gl_pathv, newn, sizeof(*pathv)); if (pathv == NULL) return (GLOB_NOSPACE); if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) { /* first time around -- clear initial gl_offs items */ pathv += pglob->gl_offs; for (i = pglob->gl_offs + 1; --i > 0; ) *--pathv = NULL; } pglob->gl_pathv = pathv; if (origpat != NULL) copy = strdup(origpat); else { for (p = path; *p++ != EOS;) continue; len = MB_CUR_MAX * (size_t)(p - path); /* XXX overallocation */ if ((copy = malloc(len)) != NULL) { if (g_Ctoc(path, copy, len)) { free(copy); errno = E2BIG; return (GLOB_NOSPACE); } } } if (copy != NULL) { limit->l_string_cnt += strlen(copy) + 1; if ((pglob->gl_flags & GLOB_LIMIT) && limit->l_string_cnt >= GLOB_LIMIT_STRING) { free(copy); errno = E2BIG; return (GLOB_NOSPACE); } pathv[pglob->gl_offs + pglob->gl_pathc++] = copy; } pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; return (copy == NULL ? GLOB_NOSPACE : 0); } /* * pattern matching function for filenames. */ static int match(Char *name, Char *pat, Char *patend) { int ok, negate_range; Char c, k, *nextp, *nextn; struct xlocale_collate *table = (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE]; nextn = NULL; nextp = NULL; while (1) { while (pat < patend) { c = *pat++; switch (c & M_MASK) { case M_ALL: if (pat == patend) return (1); if (*name == EOS) return (0); nextn = name + 1; nextp = pat - 1; break; case M_ONE: if (*name++ == EOS) goto fail; break; case M_SET: ok = 0; if ((k = *name++) == EOS) goto fail; negate_range = ((*pat & M_MASK) == M_NOT); if (negate_range != 0) ++pat; while (((c = *pat++) & M_MASK) != M_END) if ((*pat & M_MASK) == M_RNG) { if (table->__collate_load_error ? CHAR(c) <= CHAR(k) && CHAR(k) <= CHAR(pat[1]) : __wcollate_range_cmp(CHAR(c), CHAR(k)) <= 0 && __wcollate_range_cmp(CHAR(k), CHAR(pat[1])) <= 0) ok = 1; pat += 2; } else if (c == k) ok = 1; if (ok == negate_range) goto fail; break; default: if (*name++ != c) goto fail; break; } } if (*name == EOS) return (1); fail: if (nextn == NULL) break; pat = nextp; name = nextn; } return (0); } /* Free allocated data belonging to a glob_t structure. */ void globfree(glob_t *pglob) { size_t i; char **pp; if (pglob->gl_pathv != NULL) { pp = pglob->gl_pathv + pglob->gl_offs; for (i = pglob->gl_pathc; i--; ++pp) if (*pp) free(*pp); free(pglob->gl_pathv); pglob->gl_pathv = NULL; } } static DIR * g_opendir(Char *str, glob_t *pglob) { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (*str == EOS) strcpy(buf, "."); else { if (g_Ctoc(str, buf, sizeof(buf))) { errno = ENAMETOOLONG; return (NULL); } } if (pglob->gl_flags & GLOB_ALTDIRFUNC) return ((*pglob->gl_opendir)(buf)); return (opendir(buf)); } static int g_lstat(Char *fn, struct stat *sb, glob_t *pglob) { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (g_Ctoc(fn, buf, sizeof(buf))) { errno = ENAMETOOLONG; return (-1); } if (pglob->gl_flags & GLOB_ALTDIRFUNC) return((*pglob->gl_lstat)(buf, sb)); return (lstat(buf, sb)); } static int g_stat(Char *fn, struct stat *sb, glob_t *pglob) { char buf[MAXPATHLEN + MB_LEN_MAX - 1]; if (g_Ctoc(fn, buf, sizeof(buf))) { errno = ENAMETOOLONG; return (-1); } if (pglob->gl_flags & GLOB_ALTDIRFUNC) return ((*pglob->gl_stat)(buf, sb)); return (stat(buf, sb)); } static const Char * g_strchr(const Char *str, wchar_t ch) { do { if (*str == ch) return (str); } while (*str++); return (NULL); } static int g_Ctoc(const Char *str, char *buf, size_t len) { mbstate_t mbs; size_t clen; memset(&mbs, 0, sizeof(mbs)); while (len >= MB_CUR_MAX) { clen = wcrtomb(buf, CHAR(*str), &mbs); if (clen == (size_t)-1) { /* XXX See initial comment #2. */ *buf = (char)CHAR(*str); clen = 1; memset(&mbs, 0, sizeof(mbs)); } if (CHAR(*str) == EOS) return (0); str++; buf += clen; len -= clen; } return (1); } static int err_nomatch(glob_t *pglob, struct glob_limit *limit, const char *origpat) { /* * If there was no match we are going to append the origpat * if GLOB_NOCHECK was specified or if GLOB_NOMAGIC was specified * and the origpat did not contain any magic characters * GLOB_NOMAGIC is there just for compatibility with csh. */ if ((pglob->gl_flags & GLOB_NOCHECK) || ((pglob->gl_flags & GLOB_NOMAGIC) && !(pglob->gl_flags & GLOB_MAGCHAR))) return (globextend(NULL, pglob, limit, origpat)); return (GLOB_NOMATCH); } static int err_aborted(glob_t *pglob, int err, char *buf) { if ((pglob->gl_errfunc != NULL && pglob->gl_errfunc(buf, err)) || (pglob->gl_flags & GLOB_ERR)) return (GLOB_ABORTED); return (0); } #ifdef DEBUG static void qprintf(const char *str, Char *s) { Char *p; (void)printf("%s\n", str); if (s != NULL) { for (p = s; *p != EOS; p++) (void)printf("%c", (char)CHAR(*p)); (void)printf("\n"); for (p = s; *p != EOS; p++) (void)printf("%c", (isprot(*p) ? '\\' : ' ')); (void)printf("\n"); for (p = s; *p != EOS; p++) (void)printf("%c", (ismeta(*p) ? '_' : ' ')); (void)printf("\n"); } } #endif diff --git a/lib/libc/iconv/citrus_iconv.c b/lib/libc/iconv/citrus_iconv.c index 88dfc2deca33..27f88c6a47ab 100644 --- a/lib/libc/iconv/citrus_iconv.c +++ b/lib/libc/iconv/citrus_iconv.c @@ -1,375 +1,376 @@ /* $FreeBSD$ */ /* $NetBSD: citrus_iconv.c,v 1.10 2011/11/19 18:34:21 tnozaki Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c)2003 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "citrus_namespace.h" #include "citrus_bcs.h" #include "citrus_esdb.h" #include "citrus_region.h" #include "citrus_memstream.h" #include "citrus_mmap.h" #include "citrus_module.h" #include "citrus_lock.h" #include "citrus_lookup.h" #include "citrus_hash.h" #include "citrus_iconv.h" #define _CITRUS_ICONV_DIR "iconv.dir" #define _CITRUS_ICONV_ALIAS "iconv.alias" #define CI_HASH_SIZE 101 #define CI_INITIAL_MAX_REUSE 5 #define CI_ENV_MAX_REUSE "ICONV_MAX_REUSE" static bool isinit = false; static int shared_max_reuse, shared_num_unused; static _CITRUS_HASH_HEAD(, _citrus_iconv_shared, CI_HASH_SIZE) shared_pool; static TAILQ_HEAD(, _citrus_iconv_shared) shared_unused; static pthread_rwlock_t ci_lock = PTHREAD_RWLOCK_INITIALIZER; static __inline void init_cache(void) { WLOCK(&ci_lock); if (!isinit) { _CITRUS_HASH_INIT(&shared_pool, CI_HASH_SIZE); TAILQ_INIT(&shared_unused); shared_max_reuse = -1; - if (!issetugid() && getenv(CI_ENV_MAX_REUSE)) - shared_max_reuse = atoi(getenv(CI_ENV_MAX_REUSE)); + if (secure_getenv(CI_ENV_MAX_REUSE) != NULL) + shared_max_reuse = + atoi(secure_getenv(CI_ENV_MAX_REUSE)); if (shared_max_reuse < 0) shared_max_reuse = CI_INITIAL_MAX_REUSE; isinit = true; } UNLOCK(&ci_lock); } static __inline void close_shared(struct _citrus_iconv_shared *ci) { if (ci) { if (ci->ci_module) { if (ci->ci_ops) { if (ci->ci_closure) (*ci->ci_ops->io_uninit_shared)(ci); free(ci->ci_ops); } _citrus_unload_module(ci->ci_module); } free(ci); } } static __inline int open_shared(struct _citrus_iconv_shared * __restrict * __restrict rci, const char * __restrict convname, const char * __restrict src, const char * __restrict dst) { struct _citrus_iconv_shared *ci; _citrus_iconv_getops_t getops; const char *module; size_t len_convname; int ret; #ifdef INCOMPATIBLE_WITH_GNU_ICONV /* * Sadly, the gnu tools expect iconv to actually parse the * byte stream and don't allow for a pass-through when * the (src,dest) encodings are the same. * See gettext-0.18.3+ NEWS: * msgfmt now checks PO file headers more strictly with less * false-positives. * NetBSD, also, doesn't do the below pass-through. * * Also note that this currently falls short if dst options have been * specified. It may be the case that we want to ignore EILSEQ, in which * case we should also select iconv_std anyways. This trick, while * clever, may not be worth it. */ module = (strcmp(src, dst) != 0) ? "iconv_std" : "iconv_none"; #else module = "iconv_std"; #endif /* initialize iconv handle */ len_convname = strlen(convname); ci = malloc(sizeof(*ci) + len_convname + 1); if (!ci) { ret = errno; goto err; } ci->ci_module = NULL; ci->ci_ops = NULL; ci->ci_closure = NULL; ci->ci_convname = (void *)&ci[1]; memcpy(ci->ci_convname, convname, len_convname + 1); /* load module */ ret = _citrus_load_module(&ci->ci_module, module); if (ret) goto err; /* get operators */ getops = (_citrus_iconv_getops_t)_citrus_find_getops(ci->ci_module, module, "iconv"); if (!getops) { ret = EOPNOTSUPP; goto err; } ci->ci_ops = malloc(sizeof(*ci->ci_ops)); if (!ci->ci_ops) { ret = errno; goto err; } ret = (*getops)(ci->ci_ops); if (ret) goto err; if (ci->ci_ops->io_init_shared == NULL || ci->ci_ops->io_uninit_shared == NULL || ci->ci_ops->io_init_context == NULL || ci->ci_ops->io_uninit_context == NULL || ci->ci_ops->io_convert == NULL) { ret = EINVAL; goto err; } /* initialize the converter */ ret = (*ci->ci_ops->io_init_shared)(ci, src, dst); if (ret) goto err; *rci = ci; return (0); err: close_shared(ci); return (ret); } static __inline int hash_func(const char *key) { return (_string_hash_func(key, CI_HASH_SIZE)); } static __inline int match_func(struct _citrus_iconv_shared * __restrict ci, const char * __restrict key) { return (strcmp(ci->ci_convname, key)); } static int get_shared(struct _citrus_iconv_shared * __restrict * __restrict rci, const char *src, const char *dst) { struct _citrus_iconv_shared * ci; char convname[PATH_MAX]; int hashval, ret = 0; snprintf(convname, sizeof(convname), "%s/%s", src, dst); WLOCK(&ci_lock); /* lookup alread existing entry */ hashval = hash_func(convname); _CITRUS_HASH_SEARCH(&shared_pool, ci, ci_hash_entry, match_func, convname, hashval); if (ci != NULL) { /* found */ if (ci->ci_used_count == 0) { TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry); shared_num_unused--; } ci->ci_used_count++; *rci = ci; goto quit; } /* create new entry */ ret = open_shared(&ci, convname, src, dst); if (ret) goto quit; _CITRUS_HASH_INSERT(&shared_pool, ci, ci_hash_entry, hashval); ci->ci_used_count = 1; *rci = ci; quit: UNLOCK(&ci_lock); return (ret); } static void release_shared(struct _citrus_iconv_shared * __restrict ci) { WLOCK(&ci_lock); ci->ci_used_count--; if (ci->ci_used_count == 0) { /* put it into unused list */ shared_num_unused++; TAILQ_INSERT_TAIL(&shared_unused, ci, ci_tailq_entry); /* flood out */ while (shared_num_unused > shared_max_reuse) { ci = TAILQ_FIRST(&shared_unused); TAILQ_REMOVE(&shared_unused, ci, ci_tailq_entry); _CITRUS_HASH_REMOVE(ci, ci_hash_entry); shared_num_unused--; close_shared(ci); } } UNLOCK(&ci_lock); } /* * _citrus_iconv_open: * open a converter for the specified in/out codes. */ int _citrus_iconv_open(struct _citrus_iconv * __restrict * __restrict rcv, const char * __restrict src, const char * __restrict dst) { struct _citrus_iconv *cv = NULL; struct _citrus_iconv_shared *ci = NULL; char realdst[PATH_MAX], realsrc[PATH_MAX], *slashes; #ifdef _PATH_ICONV char buf[PATH_MAX], path[PATH_MAX]; #endif int ret; init_cache(); /* GNU behaviour, using locale encoding if "" or "char" is specified */ if ((strcmp(src, "") == 0) || (strcmp(src, "char") == 0)) src = nl_langinfo(CODESET); if ((strcmp(dst, "") == 0) || (strcmp(dst, "char") == 0)) dst = nl_langinfo(CODESET); strlcpy(realsrc, src, (size_t)PATH_MAX); if ((slashes = strstr(realsrc, "//")) != NULL) *slashes = '\0'; strlcpy(realdst, dst, (size_t)PATH_MAX); if ((slashes = strstr(realdst, "//")) != NULL) *slashes = '\0'; /* resolve codeset name aliases */ #ifdef _PATH_ICONV /* * Note that the below reads from realsrc and realdst while it's * repopulating (writing to) realsrc and realdst, but it's done so with * a trip through `buf`. */ snprintf(path, sizeof(path), "%s/%s", _PATH_ICONV, _CITRUS_ICONV_ALIAS); strlcpy(realsrc, _lookup_alias(path, realsrc, buf, (size_t)PATH_MAX, _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX); strlcpy(realdst, _lookup_alias(path, realdst, buf, (size_t)PATH_MAX, _LOOKUP_CASE_IGNORE), (size_t)PATH_MAX); #endif /* sanity check */ if (strchr(realsrc, '/') != NULL || strchr(realdst, '/')) return (EINVAL); /* get shared record */ ret = get_shared(&ci, realsrc, realdst); if (ret) return (ret); /* create/init context */ if (*rcv == NULL) { cv = malloc(sizeof(*cv)); if (cv == NULL) { ret = errno; release_shared(ci); return (ret); } *rcv = cv; } (*rcv)->cv_shared = ci; ret = (*ci->ci_ops->io_init_context)(*rcv); if (ret) { release_shared(ci); free(cv); return (ret); } return (0); } /* * _citrus_iconv_close: * close the specified converter. */ void _citrus_iconv_close(struct _citrus_iconv *cv) { if (cv) { (*cv->cv_shared->ci_ops->io_uninit_context)(cv); release_shared(cv->cv_shared); free(cv); } } const char *_citrus_iconv_canonicalize(const char *name) { char *buf; if ((buf = calloc((size_t)PATH_MAX, sizeof(*buf))) == NULL) return (NULL); _citrus_esdb_alias(name, buf, (size_t)PATH_MAX); return (buf); } diff --git a/lib/libc/iconv/citrus_module.c b/lib/libc/iconv/citrus_module.c index bd173b41bb04..76db1bc7df9c 100644 --- a/lib/libc/iconv/citrus_module.c +++ b/lib/libc/iconv/citrus_module.c @@ -1,317 +1,317 @@ /* $FreeBSD$ */ /* $NetBSD: citrus_module.c,v 1.9 2009/01/11 02:46:24 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c)1999, 2000, 2001, 2002 Citrus Project, * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Kranenburg. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Paul Borman at Krystal Technologies. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define I18NMODULE_MAJOR 5 #include "citrus_namespace.h" #include "citrus_bcs.h" #include "citrus_module.h" #include "libc_private.h" static int _getdewey(int[], char *); static int _cmpndewey(int[], int, int[], int); static const char *_findshlib(char *, int *, int *); static const char *_pathI18nModule = NULL; /* from libexec/ld.aout_so/shlib.c */ #undef major #undef minor #define MAXDEWEY 3 /*ELF*/ static int _getdewey(int dewey[], char *cp) { int i, n; for (n = 0, i = 0; i < MAXDEWEY; i++) { if (*cp == '\0') break; if (*cp == '.') cp++; if (*cp < '0' || '9' < *cp) return (0); dewey[n++] = (int)_bcs_strtol(cp, &cp, 10); } return (n); } /* * Compare two dewey arrays. * Return -1 if `d1' represents a smaller value than `d2'. * Return 1 if `d1' represents a greater value than `d2'. * Return 0 if equal. */ static int _cmpndewey(int d1[], int n1, int d2[], int n2) { int i; for (i = 0; i < n1 && i < n2; i++) { if (d1[i] < d2[i]) return (-1); if (d1[i] > d2[i]) return (1); } if (n1 == n2) return (0); if (i == n1) return (-1); if (i == n2) return (1); /* cannot happen */ return (0); } static const char * _findshlib(char *name, int *majorp, int *minorp) { char *lname; const char *search_dirs[1]; static char path[PATH_MAX]; int dewey[MAXDEWEY], tmp[MAXDEWEY]; int i, len, major, minor, ndewey, n_search_dirs; n_search_dirs = 1; major = *majorp; minor = *minorp; path[0] = '\0'; search_dirs[0] = _pathI18nModule; len = strlen(name); lname = name; ndewey = 0; for (i = 0; i < n_search_dirs; i++) { struct dirent *dp; DIR *dd = opendir(search_dirs[i]); int found_dot_a = 0, found_dot_so = 0; if (dd == NULL) break; while ((dp = readdir(dd)) != NULL) { int n; if (dp->d_namlen < len + 4) continue; if (strncmp(dp->d_name, lname, (size_t)len) != 0) continue; if (strncmp(dp->d_name+len, ".so.", 4) != 0) continue; if ((n = _getdewey(tmp, dp->d_name+len+4)) == 0) continue; if (major != -1 && found_dot_a) found_dot_a = 0; /* XXX should verify the library is a.out/ELF? */ if (major == -1 && minor == -1) goto compare_version; else if (major != -1 && minor == -1) { if (tmp[0] == major) goto compare_version; } else if (major != -1 && minor != -1) { if (tmp[0] == major) { if (n == 1 || tmp[1] >= minor) goto compare_version; } } /* else, this file does not qualify */ continue; compare_version: if (_cmpndewey(tmp, n, dewey, ndewey) <= 0) continue; /* We have a better version */ found_dot_so = 1; snprintf(path, sizeof(path), "%s/%s", search_dirs[i], dp->d_name); found_dot_a = 0; bcopy(tmp, dewey, sizeof(dewey)); ndewey = n; *majorp = dewey[0]; *minorp = dewey[1]; } closedir(dd); if (found_dot_a || found_dot_so) /* * There's a lib in this dir; take it. */ return (path[0] ? path : NULL); } return (path[0] ? path : NULL); } void * _citrus_find_getops(_citrus_module_t handle, const char *modname, const char *ifname) { char name[PATH_MAX]; void *p; snprintf(name, sizeof(name), "_citrus_%s_%s_getops", modname, ifname); p = dlsym((void *)handle, name); return (p); } int _citrus_load_module(_citrus_module_t *rhandle, const char *encname) { const char *p; char path[PATH_MAX]; void *handle; int maj, min; if (_pathI18nModule == NULL) { - p = getenv("PATH_I18NMODULE"); - if (p != NULL && !issetugid()) { + p = secure_getenv("PATH_I18NMODULE"); + if (p != NULL) { _pathI18nModule = strdup(p); if (_pathI18nModule == NULL) return (ENOMEM); } else _pathI18nModule = _PATH_I18NMODULE; } (void)snprintf(path, sizeof(path), "lib%s", encname); maj = I18NMODULE_MAJOR; min = -1; p = _findshlib(path, &maj, &min); if (!p) return (EINVAL); handle = libc_dlopen(p, RTLD_LAZY); if (!handle) { printf("%s", dlerror()); return (EINVAL); } *rhandle = (_citrus_module_t)handle; return (0); } void _citrus_unload_module(_citrus_module_t handle) { if (handle) dlclose((void *)handle); } diff --git a/lib/libc/locale/setlocale.c b/lib/libc/locale/setlocale.c index e0ba66e0e35a..bb60418f3583 100644 --- a/lib/libc/locale/setlocale.c +++ b/lib/libc/locale/setlocale.c @@ -1,328 +1,328 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1996 - 2002 FreeBSD Project * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Paul Borman at Krystal Technologies. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include /* for _PATH_LOCALE */ #include #include #include #include "collate.h" #include "lmonetary.h" /* for __monetary_load_locale() */ #include "lnumeric.h" /* for __numeric_load_locale() */ #include "lmessages.h" /* for __messages_load_locale() */ #include "setlocale.h" #include "ldpart.h" #include "../stdtime/timelocal.h" /* for __time_load_locale() */ /* * Category names for getenv() */ static const char categories[_LC_LAST][12] = { "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", }; /* * Current locales for each category */ static char current_categories[_LC_LAST][ENCODING_LEN + 1] = { "C", "C", "C", "C", "C", "C", "C", }; /* * Path to locale storage directory */ char *_PathLocale; /* * The locales we are going to try and load */ static char new_categories[_LC_LAST][ENCODING_LEN + 1]; static char saved_categories[_LC_LAST][ENCODING_LEN + 1]; static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)]; static char *currentlocale(void); static char *loadlocale(int); const char *__get_locale_env(int); char * setlocale(int category, const char *locale) { int i, j, len, saverr; const char *env, *r; if (category < LC_ALL || category >= _LC_LAST) { errno = EINVAL; return (NULL); } if (locale == NULL) return (category != LC_ALL ? current_categories[category] : currentlocale()); /* * Default to the current locale for everything. */ for (i = 1; i < _LC_LAST; ++i) (void)strcpy(new_categories[i], current_categories[i]); /* * Now go fill up new_categories from the locale argument */ if (!*locale) { if (category == LC_ALL) { for (i = 1; i < _LC_LAST; ++i) { env = __get_locale_env(i); if (strlen(env) > ENCODING_LEN) { errno = EINVAL; return (NULL); } (void)strcpy(new_categories[i], env); } } else { env = __get_locale_env(category); if (strlen(env) > ENCODING_LEN) { errno = EINVAL; return (NULL); } (void)strcpy(new_categories[category], env); } } else if (category != LC_ALL) { if (strlen(locale) > ENCODING_LEN) { errno = EINVAL; return (NULL); } (void)strcpy(new_categories[category], locale); } else { if ((r = strchr(locale, '/')) == NULL) { if (strlen(locale) > ENCODING_LEN) { errno = EINVAL; return (NULL); } for (i = 1; i < _LC_LAST; ++i) (void)strcpy(new_categories[i], locale); } else { for (i = 1; r[1] == '/'; ++r) ; if (!r[1]) { errno = EINVAL; return (NULL); /* Hmm, just slashes... */ } do { if (i == _LC_LAST) break; /* Too many slashes... */ if ((len = r - locale) > ENCODING_LEN) { errno = EINVAL; return (NULL); } (void)strlcpy(new_categories[i], locale, len + 1); i++; while (*r == '/') r++; locale = r; while (*r && *r != '/') r++; } while (*locale); while (i < _LC_LAST) { (void)strcpy(new_categories[i], new_categories[i - 1]); i++; } } } if (category != LC_ALL) return (loadlocale(category)); for (i = 1; i < _LC_LAST; ++i) { (void)strcpy(saved_categories[i], current_categories[i]); if (loadlocale(i) == NULL) { saverr = errno; for (j = 1; j < i; j++) { (void)strcpy(new_categories[j], saved_categories[j]); if (loadlocale(j) == NULL) { (void)strcpy(new_categories[j], "C"); (void)loadlocale(j); } } errno = saverr; return (NULL); } } return (currentlocale()); } static char * currentlocale(void) { int i; (void)strcpy(current_locale_string, current_categories[1]); for (i = 2; i < _LC_LAST; ++i) if (strcmp(current_categories[1], current_categories[i])) { for (i = 2; i < _LC_LAST; ++i) { (void)strcat(current_locale_string, "/"); (void)strcat(current_locale_string, current_categories[i]); } break; } return (current_locale_string); } static char * loadlocale(int category) { char *new = new_categories[category]; char *old = current_categories[category]; int (*func) (const char *); int saved_errno; if ((new[0] == '.' && (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || strchr(new, '/') != NULL) { errno = EINVAL; return (NULL); } saved_errno = errno; errno = __detect_path_locale(); if (errno != 0) return (NULL); errno = saved_errno; switch (category) { case LC_CTYPE: func = __wrap_setrunelocale; break; case LC_COLLATE: func = __collate_load_tables; break; case LC_TIME: func = __time_load_locale; break; case LC_NUMERIC: func = __numeric_load_locale; break; case LC_MONETARY: func = __monetary_load_locale; break; case LC_MESSAGES: func = __messages_load_locale; break; default: errno = EINVAL; return (NULL); } if (strcmp(new, old) == 0) return (old); if (func(new) != _LDP_ERROR) { (void)strcpy(old, new); (void)strcpy(__xlocale_global_locale.components[category-1]->locale, new); return (old); } return (NULL); } const char * __get_locale_env(int category) { const char *env; /* 1. check LC_ALL. */ env = getenv(categories[0]); /* 2. check LC_* */ if (env == NULL || !*env) env = getenv(categories[category]); /* 3. check LANG */ if (env == NULL || !*env) env = getenv("LANG"); /* 4. if none is set, fall to "C" */ if (env == NULL || !*env) env = "C"; return (env); } /* * Detect locale storage location and store its value to _PathLocale variable */ int __detect_path_locale(void) { if (_PathLocale == NULL) { - char *p = getenv("PATH_LOCALE"); + char *p = secure_getenv("PATH_LOCALE"); - if (p != NULL && !issetugid()) { + if (p != NULL) { if (strlen(p) + 1/*"/"*/ + ENCODING_LEN + 1/*"/"*/ + CATEGORY_LEN >= PATH_MAX) return (ENAMETOOLONG); _PathLocale = strdup(p); if (_PathLocale == NULL) return (errno == 0 ? ENOMEM : errno); } else _PathLocale = _PATH_LOCALE; } return (0); } diff --git a/lib/libc/net/hesiod.c b/lib/libc/net/hesiod.c index 0966b6d7ef91..f456e76316a1 100644 --- a/lib/libc/net/hesiod.c +++ b/lib/libc/net/hesiod.c @@ -1,583 +1,577 @@ /* $NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $ */ /* Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* Copyright 1996 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. */ /* This file is part of the hesiod library. It implements the core * portion of the hesiod resolver. * * This file is loosely based on an interim version of hesiod.c from * the BIND IRS library, which was in turn based on an earlier version * of this file. Extensive changes have been made on each step of the * path. * * This implementation is not truly thread-safe at the moment because * it uses res_send() and accesses _res. */ #include #if 0 static char *orig_rcsid = "$NetBSD: hesiod.c,v 1.9 1999/02/11 06:16:38 simonb Exp $"; #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include struct hesiod_p { char *lhs; /* normally ".ns" */ char *rhs; /* AKA the default hesiod domain */ int classes[2]; /* The class search order. */ }; #define MAX_HESRESP 1024 static int read_config_file(struct hesiod_p *, const char *); static char **get_txt_records(int, const char *); static int init_context(void); static void translate_errors(void); /* * hesiod_init -- * initialize a hesiod_p. */ int hesiod_init(context) void **context; { struct hesiod_p *ctx; const char *p, *configname; ctx = malloc(sizeof(struct hesiod_p)); if (ctx) { *context = ctx; - if (!issetugid()) - configname = getenv("HESIOD_CONFIG"); - else - configname = NULL; + configname = secure_getenv("HESIOD_CONFIG"); if (!configname) configname = _PATH_HESIOD_CONF; if (read_config_file(ctx, configname) >= 0) { /* * The default rhs can be overridden by an * environment variable. */ - if (!issetugid()) - p = getenv("HES_DOMAIN"); - else - p = NULL; + p = secure_getenv("HES_DOMAIN"); if (p) { if (ctx->rhs) free(ctx->rhs); ctx->rhs = malloc(strlen(p) + 2); if (ctx->rhs) { *ctx->rhs = '.'; strcpy(ctx->rhs + 1, (*p == '.') ? p + 1 : p); return 0; } else errno = ENOMEM; } else return 0; } } else errno = ENOMEM; if (ctx->lhs) free(ctx->lhs); if (ctx->rhs) free(ctx->rhs); if (ctx) free(ctx); return -1; } /* * hesiod_end -- * Deallocates the hesiod_p. */ void hesiod_end(context) void *context; { struct hesiod_p *ctx = (struct hesiod_p *) context; free(ctx->rhs); if (ctx->lhs) free(ctx->lhs); free(ctx); } /* * hesiod_to_bind -- * takes a hesiod (name, type) and returns a DNS * name which is to be resolved. */ char * hesiod_to_bind(void *context, const char *name, const char *type) { struct hesiod_p *ctx = (struct hesiod_p *) context; char bindname[MAXDNAME], *p, *ret, **rhs_list = NULL; const char *rhs; int len; if (strlcpy(bindname, name, sizeof(bindname)) >= sizeof(bindname)) { errno = EMSGSIZE; return NULL; } /* * Find the right right hand side to use, possibly * truncating bindname. */ p = strchr(bindname, '@'); if (p) { *p++ = 0; if (strchr(p, '.')) rhs = name + (p - bindname); else { rhs_list = hesiod_resolve(context, p, "rhs-extension"); if (rhs_list) rhs = *rhs_list; else { errno = ENOENT; return NULL; } } } else rhs = ctx->rhs; /* See if we have enough room. */ len = strlen(bindname) + 1 + strlen(type); if (ctx->lhs) len += strlen(ctx->lhs) + ((ctx->lhs[0] != '.') ? 1 : 0); len += strlen(rhs) + ((rhs[0] != '.') ? 1 : 0); if (len > sizeof(bindname) - 1) { if (rhs_list) hesiod_free_list(context, rhs_list); errno = EMSGSIZE; return NULL; } /* Put together the rest of the domain. */ strcat(bindname, "."); strcat(bindname, type); /* Only append lhs if it isn't empty. */ if (ctx->lhs && ctx->lhs[0] != '\0' ) { if (ctx->lhs[0] != '.') strcat(bindname, "."); strcat(bindname, ctx->lhs); } if (rhs[0] != '.') strcat(bindname, "."); strcat(bindname, rhs); /* rhs_list is no longer needed, since we're done with rhs. */ if (rhs_list) hesiod_free_list(context, rhs_list); /* Make a copy of the result and return it to the caller. */ ret = strdup(bindname); if (!ret) errno = ENOMEM; return ret; } /* * hesiod_resolve -- * Given a hesiod name and type, return an array of strings returned * by the resolver. */ char ** hesiod_resolve(context, name, type) void *context; const char *name; const char *type; { struct hesiod_p *ctx = (struct hesiod_p *) context; char *bindname, **retvec; bindname = hesiod_to_bind(context, name, type); if (!bindname) return NULL; retvec = get_txt_records(ctx->classes[0], bindname); if (retvec == NULL && errno == ENOENT && ctx->classes[1]) retvec = get_txt_records(ctx->classes[1], bindname); free(bindname); return retvec; } /*ARGSUSED*/ void hesiod_free_list(context, list) void *context; char **list; { char **p; if (list == NULL) return; for (p = list; *p; p++) free(*p); free(list); } /* read_config_file -- * Parse the /etc/hesiod.conf file. Returns 0 on success, * -1 on failure. On failure, it might leave values in ctx->lhs * or ctx->rhs which need to be freed by the caller. */ static int read_config_file(ctx, filename) struct hesiod_p *ctx; const char *filename; { char *key, *data, *p, **which; char buf[MAXDNAME + 7]; int n; FILE *fp; /* Set default query classes. */ ctx->classes[0] = C_IN; ctx->classes[1] = C_HS; /* Try to open the configuration file. */ fp = fopen(filename, "re"); if (!fp) { /* Use compiled in default domain names. */ ctx->lhs = strdup(DEF_LHS); ctx->rhs = strdup(DEF_RHS); if (ctx->lhs && ctx->rhs) return 0; else { errno = ENOMEM; return -1; } } ctx->lhs = NULL; ctx->rhs = NULL; while (fgets(buf, sizeof(buf), fp) != NULL) { p = buf; if (*p == '#' || *p == '\n' || *p == '\r') continue; while (*p == ' ' || *p == '\t') p++; key = p; while (*p != ' ' && *p != '\t' && *p != '=') p++; *p++ = 0; while (isspace(*p) || *p == '=') p++; data = p; while (!isspace(*p)) p++; *p = 0; if (strcasecmp(key, "lhs") == 0 || strcasecmp(key, "rhs") == 0) { which = (strcasecmp(key, "lhs") == 0) ? &ctx->lhs : &ctx->rhs; *which = strdup(data); if (!*which) { fclose(fp); errno = ENOMEM; return -1; } } else { if (strcasecmp(key, "classes") == 0) { n = 0; while (*data && n < 2) { p = data; while (*p && *p != ',') p++; if (*p) *p++ = 0; if (strcasecmp(data, "IN") == 0) ctx->classes[n++] = C_IN; else if (strcasecmp(data, "HS") == 0) ctx->classes[n++] = C_HS; data = p; } while (n < 2) ctx->classes[n++] = 0; } } } fclose(fp); if (!ctx->rhs || ctx->classes[0] == 0 || ctx->classes[0] == ctx->classes[1]) { errno = ENOEXEC; return -1; } return 0; } /* * get_txt_records -- * Given a DNS class and a DNS name, do a lookup for TXT records, and * return a list of them. */ static char ** get_txt_records(qclass, name) int qclass; const char *name; { HEADER *hp; unsigned char qbuf[PACKETSZ], abuf[MAX_HESRESP], *p, *eom, *eor; char *dst, **list; int ancount, qdcount, i, j, n, skip, type, class, len; /* Make sure the resolver is initialized. */ if ((_res.options & RES_INIT) == 0 && res_init() == -1) return NULL; /* Construct the query. */ n = res_mkquery(QUERY, name, qclass, T_TXT, NULL, 0, NULL, qbuf, PACKETSZ); if (n < 0) return NULL; /* Send the query. */ n = res_send(qbuf, n, abuf, MAX_HESRESP); if (n < 0 || n > MAX_HESRESP) { errno = ECONNREFUSED; /* XXX */ return NULL; } /* Parse the header of the result. */ hp = (HEADER *) (void *) abuf; ancount = ntohs(hp->ancount); qdcount = ntohs(hp->qdcount); p = abuf + sizeof(HEADER); eom = abuf + n; /* * Skip questions, trying to get to the answer section * which follows. */ for (i = 0; i < qdcount; i++) { skip = dn_skipname(p, eom); if (skip < 0 || p + skip + QFIXEDSZ > eom) { errno = EMSGSIZE; return NULL; } p += skip + QFIXEDSZ; } /* Allocate space for the text record answers. */ list = malloc((ancount + 1) * sizeof(char *)); if (!list) { errno = ENOMEM; return NULL; } /* Parse the answers. */ j = 0; for (i = 0; i < ancount; i++) { /* Parse the header of this answer. */ skip = dn_skipname(p, eom); if (skip < 0 || p + skip + 10 > eom) break; type = p[skip + 0] << 8 | p[skip + 1]; class = p[skip + 2] << 8 | p[skip + 3]; len = p[skip + 8] << 8 | p[skip + 9]; p += skip + 10; if (p + len > eom) { errno = EMSGSIZE; break; } /* Skip entries of the wrong class and type. */ if (class != qclass || type != T_TXT) { p += len; continue; } /* Allocate space for this answer. */ list[j] = malloc((size_t)len); if (!list[j]) { errno = ENOMEM; break; } dst = list[j++]; /* Copy answer data into the allocated area. */ eor = p + len; while (p < eor) { n = (unsigned char) *p++; if (p + n > eor) { errno = EMSGSIZE; break; } memcpy(dst, p, (size_t)n); p += n; dst += n; } if (p < eor) { errno = EMSGSIZE; break; } *dst = 0; } /* * If we didn't terminate the loop normally, something * went wrong. */ if (i < ancount) { for (i = 0; i < j; i++) free(list[i]); free(list); return NULL; } if (j == 0) { errno = ENOENT; free(list); return NULL; } list[j] = NULL; return list; } /* * COMPATIBILITY FUNCTIONS */ static int inited = 0; static void *context; static int errval = HES_ER_UNINIT; int hes_init() { init_context(); return errval; } char * hes_to_bind(name, type) const char *name; const char *type; { static char *bindname; if (init_context() < 0) return NULL; if (bindname) free(bindname); bindname = hesiod_to_bind(context, name, type); if (!bindname) translate_errors(); return bindname; } char ** hes_resolve(name, type) const char *name; const char *type; { static char **list; if (init_context() < 0) return NULL; /* * In the old Hesiod interface, the caller was responsible for * freeing the returned strings but not the vector of strings itself. */ if (list) free(list); list = hesiod_resolve(context, name, type); if (!list) translate_errors(); return list; } int hes_error() { return errval; } void hes_free(hp) char **hp; { hesiod_free_list(context, hp); } static int init_context() { if (!inited) { inited = 1; if (hesiod_init(&context) < 0) { errval = HES_ER_CONFIG; return -1; } errval = HES_ER_OK; } return 0; } static void translate_errors() { switch (errno) { case ENOENT: errval = HES_ER_NOTFOUND; break; case ECONNREFUSED: case EMSGSIZE: errval = HES_ER_NET; break; case ENOMEM: default: /* Not a good match, but the best we can do. */ errval = HES_ER_CONFIG; break; } } diff --git a/lib/libc/net/rcmd.c b/lib/libc/net/rcmd.c index e8b4ffd356c4..2a6edd66440c 100644 --- a/lib/libc/net/rcmd.c +++ b/lib/libc/net/rcmd.c @@ -1,735 +1,735 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1983, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef YP #include #include #endif #include #include "un-namespace.h" #include "libc_private.h" extern int innetgr( const char *, const char *, const char *, const char * ); #define max(a, b) ((a > b) ? a : b) int __ivaliduser(FILE *, u_int32_t, const char *, const char *); int __ivaliduser_af(FILE *,const void *, const char *, const char *, int, int); int __ivaliduser_sa(FILE *, const struct sockaddr *, socklen_t, const char *, const char *); static int __icheckhost(const struct sockaddr *, socklen_t, const char *); char paddr[NI_MAXHOST]; int rcmd(char **ahost, int rport, const char *locuser, const char *remuser, const char *cmd, int *fd2p) { return rcmd_af(ahost, rport, locuser, remuser, cmd, fd2p, AF_INET); } int rcmd_af(char **ahost, int rport, const char *locuser, const char *remuser, const char *cmd, int *fd2p, int af) { struct addrinfo hints, *res, *ai; struct sockaddr_storage from; fd_set reads; sigset_t oldmask, newmask; pid_t pid; int s, aport, lport, timo, error; char c, *p; int refused, nres; char num[8]; static char canonnamebuf[MAXDNAME]; /* is it proper here? */ /* call rcmdsh() with specified remote shell if appropriate. */ - if (!issetugid() && (p = getenv("RSH"))) { + if ((p = secure_getenv("RSH")) != NULL) { struct servent *sp = getservbyname("shell", "tcp"); if (sp && sp->s_port == rport) return (rcmdsh(ahost, rport, locuser, remuser, cmd, p)); } /* use rsh(1) if non-root and remote port is shell. */ if (geteuid()) { struct servent *sp = getservbyname("shell", "tcp"); if (sp && sp->s_port == rport) return (rcmdsh(ahost, rport, locuser, remuser, cmd, NULL)); } pid = getpid(); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_CANONNAME; hints.ai_family = af; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; (void)snprintf(num, sizeof(num), "%d", ntohs(rport)); error = getaddrinfo(*ahost, num, &hints, &res); if (error) { fprintf(stderr, "rcmd: getaddrinfo: %s\n", gai_strerror(error)); if (error == EAI_SYSTEM) fprintf(stderr, "rcmd: getaddrinfo: %s\n", strerror(errno)); return (-1); } if (res->ai_canonname && strlen(res->ai_canonname) + 1 < sizeof(canonnamebuf)) { strncpy(canonnamebuf, res->ai_canonname, sizeof(canonnamebuf)); *ahost = canonnamebuf; } nres = 0; for (ai = res; ai; ai = ai->ai_next) nres++; ai = res; refused = 0; sigemptyset(&newmask); sigaddset(&newmask, SIGURG); __libc_sigprocmask(SIG_BLOCK, (const sigset_t *)&newmask, &oldmask); for (timo = 1, lport = IPPORT_RESERVED - 1;;) { s = rresvport_af(&lport, ai->ai_family); if (s < 0) { if (errno != EAGAIN && ai->ai_next) { ai = ai->ai_next; continue; } if (errno == EAGAIN) (void)fprintf(stderr, "rcmd: socket: All ports in use\n"); else (void)fprintf(stderr, "rcmd: socket: %s\n", strerror(errno)); freeaddrinfo(res); __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); return (-1); } _fcntl(s, F_SETOWN, pid); if (_connect(s, ai->ai_addr, ai->ai_addrlen) >= 0) break; (void)_close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == ECONNREFUSED) refused = 1; if (ai->ai_next == NULL && (!refused || timo > 16)) { (void)fprintf(stderr, "%s: %s\n", *ahost, strerror(errno)); freeaddrinfo(res); __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); return (-1); } if (nres > 1) { int oerrno = errno; getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, sizeof(paddr), NULL, 0, NI_NUMERICHOST); (void)fprintf(stderr, "connect to address %s: ", paddr); errno = oerrno; perror(0); } if ((ai = ai->ai_next) == NULL) { /* refused && timo <= 16 */ struct timespec time_to_sleep, time_remaining; time_to_sleep.tv_sec = timo; time_to_sleep.tv_nsec = 0; (void)_nanosleep(&time_to_sleep, &time_remaining); timo *= 2; ai = res; refused = 0; } if (nres > 1) { getnameinfo(ai->ai_addr, ai->ai_addrlen, paddr, sizeof(paddr), NULL, 0, NI_NUMERICHOST); fprintf(stderr, "Trying %s...\n", paddr); } } lport--; if (fd2p == NULL) { _write(s, "", 1); lport = 0; } else { int s2 = rresvport_af(&lport, ai->ai_family), s3; socklen_t len = ai->ai_addrlen; int nfds; if (s2 < 0) goto bad; _listen(s2, 1); (void)snprintf(num, sizeof(num), "%d", lport); if (_write(s, num, strlen(num)+1) != strlen(num)+1) { (void)fprintf(stderr, "rcmd: write (setting up stderr): %s\n", strerror(errno)); (void)_close(s2); goto bad; } nfds = max(s, s2)+1; if(nfds > FD_SETSIZE) { fprintf(stderr, "rcmd: too many files\n"); (void)_close(s2); goto bad; } again: FD_ZERO(&reads); FD_SET(s, &reads); FD_SET(s2, &reads); errno = 0; if (_select(nfds, &reads, 0, 0, 0) < 1 || !FD_ISSET(s2, &reads)){ if (errno != 0) (void)fprintf(stderr, "rcmd: select (setting up stderr): %s\n", strerror(errno)); else (void)fprintf(stderr, "select: protocol failure in circuit setup\n"); (void)_close(s2); goto bad; } s3 = _accept(s2, (struct sockaddr *)&from, &len); switch (from.ss_family) { case AF_INET: aport = ntohs(((struct sockaddr_in *)&from)->sin_port); break; #ifdef INET6 case AF_INET6: aport = ntohs(((struct sockaddr_in6 *)&from)->sin6_port); break; #endif default: aport = 0; /* error */ break; } /* * XXX careful for ftp bounce attacks. If discovered, shut them * down and check for the real auxiliary channel to connect. */ if (aport == 20) { _close(s3); goto again; } (void)_close(s2); if (s3 < 0) { (void)fprintf(stderr, "rcmd: accept: %s\n", strerror(errno)); lport = 0; goto bad; } *fd2p = s3; if (aport >= IPPORT_RESERVED || aport < IPPORT_RESERVED / 2) { (void)fprintf(stderr, "socket: protocol failure in circuit setup.\n"); goto bad2; } } (void)_write(s, locuser, strlen(locuser)+1); (void)_write(s, remuser, strlen(remuser)+1); (void)_write(s, cmd, strlen(cmd)+1); if (_read(s, &c, 1) != 1) { (void)fprintf(stderr, "rcmd: %s: %s\n", *ahost, strerror(errno)); goto bad2; } if (c != 0) { while (_read(s, &c, 1) == 1) { (void)_write(STDERR_FILENO, &c, 1); if (c == '\n') break; } goto bad2; } __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); freeaddrinfo(res); return (s); bad2: if (lport) (void)_close(*fd2p); bad: (void)_close(s); __libc_sigprocmask(SIG_SETMASK, (const sigset_t *)&oldmask, NULL); freeaddrinfo(res); return (-1); } int rresvport(int *port) { return rresvport_af(port, AF_INET); } int rresvport_af(int *alport, int family) { int s; struct sockaddr_storage ss; u_short *sport; memset(&ss, 0, sizeof(ss)); ss.ss_family = family; switch (family) { case AF_INET: ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in); sport = &((struct sockaddr_in *)&ss)->sin_port; ((struct sockaddr_in *)&ss)->sin_addr.s_addr = INADDR_ANY; break; #ifdef INET6 case AF_INET6: ((struct sockaddr *)&ss)->sa_len = sizeof(struct sockaddr_in6); sport = &((struct sockaddr_in6 *)&ss)->sin6_port; ((struct sockaddr_in6 *)&ss)->sin6_addr = in6addr_any; break; #endif default: errno = EAFNOSUPPORT; return -1; } s = _socket(ss.ss_family, SOCK_STREAM, 0); if (s < 0) return (-1); #if 0 /* compat_exact_traditional_rresvport_semantics */ sin.sin_port = htons((u_short)*alport); if (_bind(s, (struct sockaddr *)&sin, sizeof(sin)) >= 0) return (s); if (errno != EADDRINUSE) { (void)_close(s); return (-1); } #endif *sport = 0; if (bindresvport_sa(s, (struct sockaddr *)&ss) == -1) { (void)_close(s); return (-1); } *alport = (int)ntohs(*sport); return (s); } int __check_rhosts_file = 1; char *__rcmd_errstr; int ruserok(const char *rhost, int superuser, const char *ruser, const char *luser) { struct addrinfo hints, *res, *r; int error; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ error = getaddrinfo(rhost, "0", &hints, &res); if (error) return (-1); for (r = res; r; r = r->ai_next) { if (iruserok_sa(r->ai_addr, r->ai_addrlen, superuser, ruser, luser) == 0) { freeaddrinfo(res); return (0); } } freeaddrinfo(res); return (-1); } /* * New .rhosts strategy: We are passed an ip address. We spin through * hosts.equiv and .rhosts looking for a match. When the .rhosts only * has ip addresses, we don't have to trust a nameserver. When it * contains hostnames, we spin through the list of addresses the nameserver * gives us and look for a match. * * Returns 0 if ok, -1 if not ok. */ int iruserok(unsigned long raddr, int superuser, const char *ruser, const char *luser) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); return iruserok_sa((struct sockaddr *)&sin, sin.sin_len, superuser, ruser, luser); } /* * AF independent extension of iruserok. * * Returns 0 if ok, -1 if not ok. */ int iruserok_sa(const void *ra, int rlen, int superuser, const char *ruser, const char *luser) { char *cp; struct stat sbuf; struct passwd *pwd; FILE *hostf; uid_t uid; int first; char pbuf[MAXPATHLEN]; const struct sockaddr *raddr; struct sockaddr_storage ss; /* avoid alignment issue */ if (rlen <= 0 || rlen > sizeof(ss)) return (-1); memcpy(&ss, ra, rlen); raddr = (struct sockaddr *)&ss; first = 1; hostf = superuser ? NULL : fopen(_PATH_HEQUIV, "re"); again: if (hostf) { if (__ivaliduser_sa(hostf, raddr, rlen, luser, ruser) == 0) { (void)fclose(hostf); return (0); } (void)fclose(hostf); } if (first == 1 && (__check_rhosts_file || superuser)) { first = 0; if ((pwd = getpwnam(luser)) == NULL) return (-1); (void)strlcpy(pbuf, pwd->pw_dir, sizeof(pbuf)); (void)strlcat(pbuf, "/.rhosts", sizeof(pbuf)); /* * Change effective uid while opening .rhosts. If root and * reading an NFS mounted file system, can't read files that * are protected read/write owner only. */ uid = geteuid(); (void)seteuid(pwd->pw_uid); hostf = fopen(pbuf, "re"); (void)seteuid(uid); if (hostf == NULL) return (-1); /* * If not a regular file, or is owned by someone other than * user or root or if writeable by anyone but the owner, quit. */ cp = NULL; if (lstat(pbuf, &sbuf) < 0) cp = ".rhosts lstat failed"; else if (!S_ISREG(sbuf.st_mode)) cp = ".rhosts not regular file"; else if (_fstat(fileno(hostf), &sbuf) < 0) cp = ".rhosts fstat failed"; else if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) cp = "bad .rhosts owner"; else if (sbuf.st_mode & (S_IWGRP|S_IWOTH)) cp = ".rhosts writeable by other than owner"; /* If there were any problems, quit. */ if (cp) { __rcmd_errstr = cp; (void)fclose(hostf); return (-1); } goto again; } return (-1); } /* * XXX * Don't make static, used by lpd(8). * * Returns 0 if ok, -1 if not ok. */ int __ivaliduser(FILE *hostf, u_int32_t raddr, const char *luser, const char *ruser) { struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); memcpy(&sin.sin_addr, &raddr, sizeof(sin.sin_addr)); return __ivaliduser_sa(hostf, (struct sockaddr *)&sin, sin.sin_len, luser, ruser); } /* * Returns 0 if ok, -1 if not ok. * * XXX obsolete API. */ int __ivaliduser_af(FILE *hostf, const void *raddr, const char *luser, const char *ruser, int af, int len) { struct sockaddr *sa = NULL; struct sockaddr_in *sin = NULL; #ifdef INET6 struct sockaddr_in6 *sin6 = NULL; #endif struct sockaddr_storage ss; memset(&ss, 0, sizeof(ss)); switch (af) { case AF_INET: if (len != sizeof(sin->sin_addr)) return -1; sin = (struct sockaddr_in *)&ss; sin->sin_family = AF_INET; sin->sin_len = sizeof(struct sockaddr_in); memcpy(&sin->sin_addr, raddr, sizeof(sin->sin_addr)); break; #ifdef INET6 case AF_INET6: if (len != sizeof(sin6->sin6_addr)) return -1; /* you will lose scope info */ sin6 = (struct sockaddr_in6 *)&ss; sin6->sin6_family = AF_INET6; sin6->sin6_len = sizeof(struct sockaddr_in6); memcpy(&sin6->sin6_addr, raddr, sizeof(sin6->sin6_addr)); break; #endif default: return -1; } sa = (struct sockaddr *)&ss; return __ivaliduser_sa(hostf, sa, sa->sa_len, luser, ruser); } int __ivaliduser_sa(FILE *hostf, const struct sockaddr *raddr, socklen_t salen, const char *luser, const char *ruser) { char *user, *p; int ch; char buf[MAXHOSTNAMELEN + 128]; /* host + login */ char hname[MAXHOSTNAMELEN]; /* Presumed guilty until proven innocent. */ int userok = 0, hostok = 0; #ifdef YP char *ypdomain; if (yp_get_default_domain(&ypdomain)) ypdomain = NULL; #else #define ypdomain NULL #endif /* We need to get the damn hostname back for netgroup matching. */ if (getnameinfo(raddr, salen, hname, sizeof(hname), NULL, 0, NI_NAMEREQD) != 0) hname[0] = '\0'; while (fgets(buf, sizeof(buf), hostf)) { p = buf; /* Skip lines that are too long. */ if (strchr(p, '\n') == NULL) { while ((ch = getc(hostf)) != '\n' && ch != EOF); continue; } if (*p == '\n' || *p == '#') { /* comment... */ continue; } while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') { *p = isupper((unsigned char)*p) ? tolower((unsigned char)*p) : *p; p++; } if (*p == ' ' || *p == '\t') { *p++ = '\0'; while (*p == ' ' || *p == '\t') p++; user = p; while (*p != '\n' && *p != ' ' && *p != '\t' && *p != '\0') p++; } else user = p; *p = '\0'; /* * Do +/- and +@/-@ checking. This looks really nasty, * but it matches SunOS's behavior so far as I can tell. */ switch(buf[0]) { case '+': if (!buf[1]) { /* '+' matches all hosts */ hostok = 1; break; } if (buf[1] == '@') /* match a host by netgroup */ hostok = hname[0] != '\0' && innetgr(&buf[2], hname, NULL, ypdomain); else /* match a host by addr */ hostok = __icheckhost(raddr, salen, (char *)&buf[1]); break; case '-': /* reject '-' hosts and all their users */ if (buf[1] == '@') { if (hname[0] == '\0' || innetgr(&buf[2], hname, NULL, ypdomain)) return(-1); } else { if (__icheckhost(raddr, salen, (char *)&buf[1])) return(-1); } break; default: /* if no '+' or '-', do a simple match */ hostok = __icheckhost(raddr, salen, buf); break; } switch(*user) { case '+': if (!*(user+1)) { /* '+' matches all users */ userok = 1; break; } if (*(user+1) == '@') /* match a user by netgroup */ userok = innetgr(user+2, NULL, ruser, ypdomain); else /* match a user by direct specification */ userok = !(strcmp(ruser, user+1)); break; case '-': /* if we matched a hostname, */ if (hostok) { /* check for user field rejections */ if (!*(user+1)) return(-1); if (*(user+1) == '@') { if (innetgr(user+2, NULL, ruser, ypdomain)) return(-1); } else { if (!strcmp(ruser, user+1)) return(-1); } } break; default: /* no rejections: try to match the user */ if (hostok) userok = !(strcmp(ruser,*user ? user : luser)); break; } if (hostok && userok) return(0); } return (-1); } /* * Returns "true" if match, 0 if no match. */ static int __icheckhost(const struct sockaddr *raddr, socklen_t salen, const char *lhost) { struct sockaddr_in sin; struct sockaddr_in6 *sin6; struct addrinfo hints, *res, *r; int error; char h1[NI_MAXHOST], h2[NI_MAXHOST]; if (raddr->sa_family == AF_INET6) { sin6 = (struct sockaddr_in6 *)raddr; if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(struct sockaddr_in); memcpy(&sin.sin_addr, &sin6->sin6_addr.s6_addr[12], sizeof(sin.sin_addr)); raddr = (struct sockaddr *)&sin; salen = sin.sin_len; } } h1[0] = '\0'; if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, NI_NUMERICHOST) != 0) return (0); /* Resolve laddr into sockaddr */ memset(&hints, 0, sizeof(hints)); hints.ai_family = raddr->sa_family; hints.ai_socktype = SOCK_DGRAM; /*XXX dummy*/ res = NULL; error = getaddrinfo(lhost, "0", &hints, &res); if (error) return (0); for (r = res; r ; r = r->ai_next) { h2[0] = '\0'; if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), NULL, 0, NI_NUMERICHOST) != 0) continue; if (strcmp(h1, h2) == 0) { freeaddrinfo(res); return (1); } } /* No match. */ freeaddrinfo(res); return (0); } diff --git a/lib/libc/nls/msgcat.c b/lib/libc/nls/msgcat.c index f27bf7918b88..7f687258e5c3 100644 --- a/lib/libc/nls/msgcat.c +++ b/lib/libc/nls/msgcat.c @@ -1,484 +1,484 @@ /*********************************************************** Copyright 1990, by Alfalfa Software Incorporated, Cambridge, Massachusetts. Copyright 2010, Gabor Kovesdan All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that Alfalfa's name not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. ALPHALPHA DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL ALPHALPHA BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. If you make any modifications, bugfixes or other changes to this software we'd appreciate it if you could send a copy to us so we can keep things up-to-date. Many thanks. Kee Hinckley Alfalfa Software, Inc. 267 Allston St., #3 Cambridge, MA 02139 USA nazgul@alfalfa.com ******************************************************************/ #include __FBSDID("$FreeBSD$"); #define _NLS_PRIVATE #include "namespace.h" #include #include #include #include #include /* for ntohl() */ #include #include #include #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "../locale/xlocale_private.h" #include "libc_private.h" #define _DEFAULT_NLS_PATH "/usr/share/nls/%L/%N.cat:/usr/share/nls/%N/%L:" \ _PATH_LOCALBASE "/share/nls/%L/%N.cat:" \ _PATH_LOCALBASE "/share/nls/%N/%L" #define RLOCK(fail) { int ret; \ if (__isthreaded && \ ((ret = _pthread_rwlock_rdlock(&rwlock)) != 0)) { \ errno = ret; \ return (fail); \ }} #define WLOCK(fail) { int ret; \ if (__isthreaded && \ ((ret = _pthread_rwlock_wrlock(&rwlock)) != 0)) { \ errno = ret; \ return (fail); \ }} #define UNLOCK { if (__isthreaded) \ _pthread_rwlock_unlock(&rwlock); } #define NLERR ((nl_catd) -1) #define NLRETERR(errc) { errno = errc; return (NLERR); } #define SAVEFAIL(n, l, e) { np = calloc(1, sizeof(struct catentry)); \ if (np != NULL) { \ np->name = strdup(n); \ np->catd = NLERR; \ np->lang = (l == NULL) ? NULL : \ strdup(l); \ np->caterrno = e; \ if (np->name == NULL || \ (l != NULL && np->lang == NULL)) { \ free(np->name); \ free(np->lang); \ free(np); \ } else { \ WLOCK(NLERR); \ SLIST_INSERT_HEAD(&cache, np, \ list); \ UNLOCK; \ } \ } \ errno = e; \ } static nl_catd load_msgcat(const char *, const char *, const char *); static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; struct catentry { SLIST_ENTRY(catentry) list; char *name; char *path; int caterrno; nl_catd catd; char *lang; int refcount; }; SLIST_HEAD(listhead, catentry) cache = SLIST_HEAD_INITIALIZER(cache); nl_catd catopen(const char *name, int type) { return (__catopen_l(name, type, __get_locale())); } nl_catd __catopen_l(const char *name, int type, locale_t locale) { struct stat sbuf; struct catentry *np; char *base, *cptr, *cptr1, *nlspath, *pathP, *pcode; char *plang, *pter; int saverr, spcleft; const char *lang, *tmpptr; char path[PATH_MAX]; /* sanity checking */ if (name == NULL || *name == '\0') NLRETERR(EINVAL); if (strchr(name, '/') != NULL) /* have a pathname */ lang = NULL; else { if (type == NL_CAT_LOCALE) lang = querylocale(LC_MESSAGES_MASK, locale); else lang = getenv("LANG"); if (lang == NULL || *lang == '\0' || strlen(lang) > ENCODING_LEN || (lang[0] == '.' && (lang[1] == '\0' || (lang[1] == '.' && lang[2] == '\0'))) || strchr(lang, '/') != NULL) lang = "C"; } /* Try to get it from the cache first */ RLOCK(NLERR); SLIST_FOREACH(np, &cache, list) { if ((strcmp(np->name, name) == 0) && ((lang != NULL && np->lang != NULL && strcmp(np->lang, lang) == 0) || (np->lang == lang))) { if (np->caterrno != 0) { /* Found cached failing entry */ UNLOCK; NLRETERR(np->caterrno); } else { /* Found cached successful entry */ atomic_add_int(&np->refcount, 1); UNLOCK; return (np->catd); } } } UNLOCK; /* is it absolute path ? if yes, load immediately */ if (strchr(name, '/') != NULL) return (load_msgcat(name, name, lang)); /* sanity checking */ if ((plang = cptr1 = strdup(lang)) == NULL) return (NLERR); if ((cptr = strchr(cptr1, '@')) != NULL) *cptr = '\0'; pter = pcode = ""; if ((cptr = strchr(cptr1, '_')) != NULL) { *cptr++ = '\0'; pter = cptr1 = cptr; } if ((cptr = strchr(cptr1, '.')) != NULL) { *cptr++ = '\0'; pcode = cptr; } - if ((nlspath = getenv("NLSPATH")) == NULL || issetugid()) + if ((nlspath = secure_getenv("NLSPATH")) == NULL) nlspath = _DEFAULT_NLS_PATH; if ((base = cptr = strdup(nlspath)) == NULL) { saverr = errno; free(plang); errno = saverr; return (NLERR); } while ((nlspath = strsep(&cptr, ":")) != NULL) { pathP = path; if (*nlspath) { for (; *nlspath; ++nlspath) { if (*nlspath == '%') { switch (*(nlspath + 1)) { case 'l': tmpptr = plang; break; case 't': tmpptr = pter; break; case 'c': tmpptr = pcode; break; case 'L': tmpptr = lang; break; case 'N': tmpptr = (char *)name; break; case '%': ++nlspath; /* FALLTHROUGH */ default: if (pathP - path >= sizeof(path) - 1) goto too_long; *(pathP++) = *nlspath; continue; } ++nlspath; put_tmpptr: spcleft = sizeof(path) - (pathP - path) - 1; if (strlcpy(pathP, tmpptr, spcleft) >= spcleft) { too_long: free(plang); free(base); SAVEFAIL(name, lang, ENAMETOOLONG); NLRETERR(ENAMETOOLONG); } pathP += strlen(tmpptr); } else { if (pathP - path >= sizeof(path) - 1) goto too_long; *(pathP++) = *nlspath; } } *pathP = '\0'; if (stat(path, &sbuf) == 0) { free(plang); free(base); return (load_msgcat(path, name, lang)); } } else { tmpptr = (char *)name; --nlspath; goto put_tmpptr; } } free(plang); free(base); SAVEFAIL(name, lang, ENOENT); NLRETERR(ENOENT); } char * catgets(nl_catd catd, int set_id, int msg_id, const char *s) { struct _nls_cat_hdr *cat_hdr; struct _nls_msg_hdr *msg_hdr; struct _nls_set_hdr *set_hdr; int i, l, r, u; if (catd == NULL || catd == NLERR) { errno = EBADF; /* LINTED interface problem */ return ((char *)s); } cat_hdr = (struct _nls_cat_hdr *)catd->__data; set_hdr = (struct _nls_set_hdr *)(void *)((char *)catd->__data + sizeof(struct _nls_cat_hdr)); /* binary search, see knuth algorithm b */ l = 0; u = ntohl((u_int32_t)cat_hdr->__nsets) - 1; while (l <= u) { i = (l + u) / 2; r = set_id - ntohl((u_int32_t)set_hdr[i].__setno); if (r == 0) { msg_hdr = (struct _nls_msg_hdr *) (void *)((char *)catd->__data + sizeof(struct _nls_cat_hdr) + ntohl((u_int32_t)cat_hdr->__msg_hdr_offset)); l = ntohl((u_int32_t)set_hdr[i].__index); u = l + ntohl((u_int32_t)set_hdr[i].__nmsgs) - 1; while (l <= u) { i = (l + u) / 2; r = msg_id - ntohl((u_int32_t)msg_hdr[i].__msgno); if (r == 0) { return ((char *) catd->__data + sizeof(struct _nls_cat_hdr) + ntohl((u_int32_t) cat_hdr->__msg_txt_offset) + ntohl((u_int32_t) msg_hdr[i].__offset)); } else if (r < 0) { u = i - 1; } else { l = i + 1; } } /* not found */ goto notfound; } else if (r < 0) { u = i - 1; } else { l = i + 1; } } notfound: /* not found */ errno = ENOMSG; /* LINTED interface problem */ return ((char *)s); } static void catfree(struct catentry *np) { if (np->catd != NULL && np->catd != NLERR) { munmap(np->catd->__data, (size_t)np->catd->__size); free(np->catd); } SLIST_REMOVE(&cache, np, catentry, list); free(np->name); free(np->path); free(np->lang); free(np); } int catclose(nl_catd catd) { struct catentry *np; /* sanity checking */ if (catd == NULL || catd == NLERR) { errno = EBADF; return (-1); } /* Remove from cache if not referenced any more */ WLOCK(-1); SLIST_FOREACH(np, &cache, list) { if (catd == np->catd) { if (atomic_fetchadd_int(&np->refcount, -1) == 1) catfree(np); break; } } UNLOCK; return (0); } /* * Internal support functions */ static nl_catd load_msgcat(const char *path, const char *name, const char *lang) { struct stat st; nl_catd catd; struct catentry *np; void *data; char *copy_path, *copy_name, *copy_lang; int fd; /* path/name will never be NULL here */ /* * One more try in cache; if it was not found by name, * it might still be found by absolute path. */ RLOCK(NLERR); SLIST_FOREACH(np, &cache, list) { if ((np->path != NULL) && (strcmp(np->path, path) == 0)) { atomic_add_int(&np->refcount, 1); UNLOCK; return (np->catd); } } UNLOCK; if ((fd = _open(path, O_RDONLY | O_CLOEXEC)) == -1) { SAVEFAIL(name, lang, errno); NLRETERR(errno); } if (_fstat(fd, &st) != 0) { _close(fd); SAVEFAIL(name, lang, EFTYPE); NLRETERR(EFTYPE); } /* * If the file size cannot be held in size_t we cannot mmap() * it to the memory. Probably, this will not be a problem given * that catalog files are usually small. */ if (st.st_size > SIZE_T_MAX) { _close(fd); SAVEFAIL(name, lang, EFBIG); NLRETERR(EFBIG); } if ((data = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) { int saved_errno = errno; _close(fd); SAVEFAIL(name, lang, saved_errno); NLRETERR(saved_errno); } _close(fd); if (ntohl((u_int32_t)((struct _nls_cat_hdr *)data)->__magic) != _NLS_MAGIC) { munmap(data, (size_t)st.st_size); SAVEFAIL(name, lang, EFTYPE); NLRETERR(EFTYPE); } copy_name = strdup(name); copy_path = strdup(path); copy_lang = (lang == NULL) ? NULL : strdup(lang); catd = malloc(sizeof (*catd)); np = calloc(1, sizeof(struct catentry)); if (copy_name == NULL || copy_path == NULL || (lang != NULL && copy_lang == NULL) || catd == NULL || np == NULL) { free(copy_name); free(copy_path); free(copy_lang); free(catd); free(np); munmap(data, (size_t)st.st_size); SAVEFAIL(name, lang, ENOMEM); NLRETERR(ENOMEM); } catd->__data = data; catd->__size = (int)st.st_size; /* Caching opened catalog */ np->name = copy_name; np->path = copy_path; np->catd = catd; np->lang = copy_lang; atomic_store_int(&np->refcount, 1); WLOCK(NLERR); SLIST_INSERT_HEAD(&cache, np, list); UNLOCK; return (catd); } diff --git a/lib/libc/posix1e/mac.c b/lib/libc/posix1e/mac.c index a8e0abe7afff..7747b62b7c72 100644 --- a/lib/libc/posix1e/mac.c +++ b/lib/libc/posix1e/mac.c @@ -1,451 +1,450 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson * Copyright (c) 2002, 2003 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include static int internal_initialized; /* * Maintain a list of default label preparations for various object * types. Each name will appear only once in the list. * * XXXMAC: Not thread-safe. */ static LIST_HEAD(, label_default) label_default_head; struct label_default { char *ld_name; char *ld_labels; LIST_ENTRY(label_default) ld_entries; }; static void mac_destroy_labels(void) { struct label_default *ld; while ((ld = LIST_FIRST(&label_default_head))) { free(ld->ld_name); free(ld->ld_labels); LIST_REMOVE(ld, ld_entries); free(ld); } } static void mac_destroy_internal(void) { mac_destroy_labels(); internal_initialized = 0; } static int mac_add_type(const char *name, const char *labels) { struct label_default *ld, *ld_new; char *name_dup, *labels_dup; /* * Speculatively allocate all the memory now to avoid allocating * later when we will someday hold a mutex. */ name_dup = strdup(name); if (name_dup == NULL) { errno = ENOMEM; return (-1); } labels_dup = strdup(labels); if (labels_dup == NULL) { free(name_dup); errno = ENOMEM; return (-1); } ld_new = malloc(sizeof(*ld)); if (ld_new == NULL) { free(name_dup); free(labels_dup); errno = ENOMEM; return (-1); } /* * If the type is already present, replace the current entry * rather than add a new instance. */ for (ld = LIST_FIRST(&label_default_head); ld != NULL; ld = LIST_NEXT(ld, ld_entries)) { if (strcmp(name, ld->ld_name) == 0) break; } if (ld != NULL) { free(ld->ld_labels); ld->ld_labels = labels_dup; labels_dup = NULL; } else { ld = ld_new; ld->ld_name = name_dup; ld->ld_labels = labels_dup; ld_new = NULL; name_dup = NULL; labels_dup = NULL; LIST_INSERT_HEAD(&label_default_head, ld, ld_entries); } if (name_dup != NULL) free(name_dup); if (labels_dup != NULL) free(labels_dup); if (ld_new != NULL) free(ld_new); return (0); } static char * next_token(char **string) { char *token; token = strsep(string, " \t"); while (token != NULL && *token == '\0') token = strsep(string, " \t"); return (token); } static int mac_init_internal(int ignore_errors) { const char *filename; char line[LINE_MAX]; FILE *file; int error; error = 0; LIST_INIT(&label_default_head); - if (!issetugid() && getenv("MAC_CONFFILE") != NULL) - filename = getenv("MAC_CONFFILE"); - else + filename = secure_getenv("MAC_CONFFILE"); + if (filename == NULL) filename = MAC_CONFFILE; file = fopen(filename, "re"); if (file == NULL) return (0); while (fgets(line, LINE_MAX, file)) { char *comment, *parse, *statement; if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0'; else { if (ignore_errors) continue; fclose(file); error = EINVAL; goto just_return; } /* Remove any comment. */ comment = line; parse = strsep(&comment, "#"); /* Blank lines OK. */ statement = next_token(&parse); if (statement == NULL) continue; if (strcmp(statement, "default_labels") == 0) { char *name, *labels; name = next_token(&parse); labels = next_token(&parse); if (name == NULL || labels == NULL || next_token(&parse) != NULL) { if (ignore_errors) continue; error = EINVAL; fclose(file); goto just_return; } if (mac_add_type(name, labels) == -1) { if (ignore_errors) continue; fclose(file); goto just_return; } } else if (strcmp(statement, "default_ifnet_labels") == 0 || strcmp(statement, "default_file_labels") == 0 || strcmp(statement, "default_process_labels") == 0) { char *labels, *type; if (strcmp(statement, "default_ifnet_labels") == 0) type = "ifnet"; else if (strcmp(statement, "default_file_labels") == 0) type = "file"; else if (strcmp(statement, "default_process_labels") == 0) type = "process"; labels = next_token(&parse); if (labels == NULL || next_token(&parse) != NULL) { if (ignore_errors) continue; error = EINVAL; fclose(file); goto just_return; } if (mac_add_type(type, labels) == -1) { if (ignore_errors) continue; fclose(file); goto just_return; } } else { if (ignore_errors) continue; fclose(file); error = EINVAL; goto just_return; } } fclose(file); internal_initialized = 1; just_return: if (error != 0) mac_destroy_internal(); return (error); } static int mac_maybe_init_internal(void) { if (!internal_initialized) return (mac_init_internal(1)); else return (0); } int mac_reload(void) { if (internal_initialized) mac_destroy_internal(); return (mac_init_internal(0)); } int mac_free(struct mac *mac) { if (mac->m_string != NULL) free(mac->m_string); free(mac); return (0); } int mac_from_text(struct mac **mac, const char *text) { *mac = (struct mac *) malloc(sizeof(**mac)); if (*mac == NULL) return (ENOMEM); (*mac)->m_string = strdup(text); if ((*mac)->m_string == NULL) { free(*mac); *mac = NULL; return (ENOMEM); } (*mac)->m_buflen = strlen((*mac)->m_string)+1; return (0); } int mac_to_text(struct mac *mac, char **text) { *text = strdup(mac->m_string); if (*text == NULL) return (ENOMEM); return (0); } int mac_prepare(struct mac **mac, const char *elements) { if (strlen(elements) >= MAC_MAX_LABEL_BUF_LEN) return (EINVAL); *mac = (struct mac *) malloc(sizeof(**mac)); if (*mac == NULL) return (ENOMEM); (*mac)->m_string = malloc(MAC_MAX_LABEL_BUF_LEN); if ((*mac)->m_string == NULL) { free(*mac); *mac = NULL; return (ENOMEM); } strcpy((*mac)->m_string, elements); (*mac)->m_buflen = MAC_MAX_LABEL_BUF_LEN; return (0); } int mac_prepare_type(struct mac **mac, const char *name) { struct label_default *ld; int error; error = mac_maybe_init_internal(); if (error != 0) return (error); for (ld = LIST_FIRST(&label_default_head); ld != NULL; ld = LIST_NEXT(ld, ld_entries)) { if (strcmp(name, ld->ld_name) == 0) return (mac_prepare(mac, ld->ld_labels)); } errno = ENOENT; return (-1); /* XXXMAC: ENOLABEL */ } int mac_prepare_ifnet_label(struct mac **mac) { return (mac_prepare_type(mac, "ifnet")); } int mac_prepare_file_label(struct mac **mac) { return (mac_prepare_type(mac, "file")); } int mac_prepare_packet_label(struct mac **mac) { return (mac_prepare_type(mac, "packet")); } int mac_prepare_process_label(struct mac **mac) { return (mac_prepare_type(mac, "process")); } /* * Simply test whether the TrustedBSD/MAC MIB tree is present; if so, * return 1 to indicate that the system has MAC enabled overall or for * a given policy. */ int mac_is_present(const char *policyname) { int mib[5]; size_t siz; char *mibname; int error; if (policyname != NULL) { if (policyname[strcspn(policyname, ".=")] != '\0') { errno = EINVAL; return (-1); } mibname = malloc(sizeof("security.mac.") - 1 + strlen(policyname) + sizeof(".enabled")); if (mibname == NULL) return (-1); strcpy(mibname, "security.mac."); strcat(mibname, policyname); strcat(mibname, ".enabled"); siz = 5; error = sysctlnametomib(mibname, mib, &siz); free(mibname); } else { siz = 3; error = sysctlnametomib("security.mac", mib, &siz); } if (error == -1) { switch (errno) { case ENOTDIR: case ENOENT: return (0); default: return (error); } } return (1); } diff --git a/lib/libc/resolv/res_init.c b/lib/libc/resolv/res_init.c index 274ffbf999d6..40d3373e813d 100644 --- a/lib/libc/resolv/res_init.c +++ b/lib/libc/resolv/res_init.c @@ -1,944 +1,944 @@ /*- * SPDX-License-Identifier: (BSD-3-Clause AND ISC) * * Copyright (c) 1985, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (c) 1996-1999 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_init.c 8.1 (Berkeley) 6/7/93"; static const char rcsid[] = "$Id: res_init.c,v 1.26 2008/12/11 09:59:00 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include "namespace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef HAVE_MD5 # include "../dst/md5.h" #else # ifdef SOLARIS2 # include # elif _LIBC # include # endif #endif #ifndef _MD5_H_ # define _MD5_H_ 1 /*%< make sure we do not include rsaref md5.h file */ #endif #include "un-namespace.h" #include "port_after.h" /* ensure that sockaddr_in6 and IN6ADDR_ANY_INIT are declared / defined */ #include #include "res_private.h" /*% Options. Should all be left alone. */ #define RESOLVSORT #ifndef DEBUG #define DEBUG #endif #ifdef SOLARIS2 #include #endif static void res_setoptions(res_state, const char *, const char *); #ifdef RESOLVSORT static const char sort_mask[] = "/&"; #define ISSORTMASK(ch) (strchr(sort_mask, ch) != NULL) static u_int32_t net_mask(struct in_addr); #endif #if !defined(isascii) /*%< XXX - could be a function */ # define isascii(c) (!(c & 0200)) #endif /* * Resolver state default settings. */ /*% * Set up default settings. If the configuration file exist, the values * there will have precedence. Otherwise, the server address is set to * INADDR_ANY and the default domain name comes from the gethostname(). * * An interim version of this code (BIND 4.9, pre-4.4BSD) used 127.0.0.1 * rather than INADDR_ANY ("0.0.0.0") as the default name server address * since it was noted that INADDR_ANY actually meant ``the first interface * you "ifconfig"'d at boot time'' and if this was a SLIP or PPP interface, * it had to be "up" in order for you to reach your own name server. It * was later decided that since the recommended practice is to always * install local static routes through 127.0.0.1 for all your network * interfaces, that we could solve this problem without a code change. * * The configuration file should always be used, since it is the only way * to specify a default domain. If you are running a server on your local * machine, you should say "nameserver 0.0.0.0" or "nameserver 127.0.0.1" * in the configuration file. * * Return 0 if completes successfully, -1 on error */ int res_ninit(res_state statp) { extern int __res_vinit(res_state, int); return (__res_vinit(statp, 0)); } /*% This function has to be reachable by res_data.c but not publicly. */ int __res_vinit(res_state statp, int preinit) { FILE *fp; char *cp, **pp; int n; char buf[BUFSIZ]; int nserv = 0; /*%< number of nameserver records read from file */ int haveenv = 0; int havesearch = 0; #ifdef RESOLVSORT int nsort = 0; char *net; #endif int dots; union res_sockaddr_union u[2]; int maxns = MAXNS; RES_SET_H_ERRNO(statp, 0); if (statp->_u._ext.ext != NULL) res_ndestroy(statp); if (!preinit) { statp->retrans = RES_TIMEOUT; statp->retry = RES_DFLRETRY; statp->options = RES_DEFAULT; } statp->_rnd = malloc(16); res_rndinit(statp); statp->id = res_nrandomid(statp); memset(u, 0, sizeof(u)); u[nserv].sin.sin_addr.s_addr = INADDR_ANY; u[nserv].sin.sin_family = AF_INET; u[nserv].sin.sin_port = htons(NAMESERVER_PORT); #ifdef HAVE_SA_LEN u[nserv].sin.sin_len = sizeof(struct sockaddr_in); #endif nserv++; #ifdef HAS_INET6_STRUCTS u[nserv].sin6.sin6_addr = in6addr_any; u[nserv].sin6.sin6_family = AF_INET6; u[nserv].sin6.sin6_port = htons(NAMESERVER_PORT); #ifdef HAVE_SA_LEN u[nserv].sin6.sin6_len = sizeof(struct sockaddr_in6); #endif nserv++; #endif statp->nscount = 0; statp->ndots = 1; statp->pfcode = 0; statp->_vcsock = -1; statp->_flags = 0; statp->qhook = NULL; statp->rhook = NULL; statp->_u._ext.nscount = 0; statp->_u._ext.ext = malloc(sizeof(*statp->_u._ext.ext)); if (statp->_u._ext.ext != NULL) { memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext)); statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr; strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa"); strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int"); statp->_u._ext.ext->reload_period = 2; } else { /* * Historically res_init() rarely, if at all, failed. * Examples and applications exist which do not check * our return code. Furthermore several applications * simply call us to get the systems domainname. So * rather then immediately fail here we store the * failure, which is returned later, in h_errno. And * prevent the collection of 'nameserver' information * by setting maxns to 0. Thus applications that fail * to check our return code wont be able to make * queries anyhow. */ RES_SET_H_ERRNO(statp, NETDB_INTERNAL); maxns = 0; } #ifdef RESOLVSORT statp->nsort = 0; #endif res_setservers(statp, u, nserv); #ifdef SOLARIS2 /* * The old libresolv derived the defaultdomain from NIS/NIS+. * We want to keep this behaviour */ { char buf[sizeof(statp->defdname)], *cp; int ret; if ((ret = sysinfo(SI_SRPC_DOMAIN, buf, sizeof(buf))) > 0 && (unsigned int)ret <= sizeof(buf)) { if (buf[0] == '+') buf[0] = '.'; cp = strchr(buf, '.'); cp = (cp == NULL) ? buf : (cp + 1); strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; } } #endif /* SOLARIS2 */ /* Allow user to override the local domain definition */ - if (issetugid() == 0 && (cp = getenv("LOCALDOMAIN")) != NULL) { + if ((cp = secure_getenv("LOCALDOMAIN")) != NULL) { (void)strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; haveenv++; /* * Set search list to be blank-separated strings * from rest of env value. Permits users of LOCALDOMAIN * to still have a search list, and anyone to set the * one that they want to use as an individual (even more * important now that the rfc1535 stuff restricts searches) */ cp = statp->defdname; pp = statp->dnsrch; *pp++ = cp; for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) { if (*cp == '\n') /*%< silly backwards compat */ break; else if (*cp == ' ' || *cp == '\t') { *cp = 0; n = 1; } else if (n) { *pp++ = cp; n = 0; havesearch = 1; } } /* null terminate last domain if there are excess */ while (*cp != '\0' && *cp != ' ' && *cp != '\t' && *cp != '\n') cp++; *cp = '\0'; *pp++ = NULL; } #define MATCH(line, name) \ (!strncmp(line, name, sizeof(name) - 1) && \ (line[sizeof(name) - 1] == ' ' || \ line[sizeof(name) - 1] == '\t')) nserv = 0; if ((fp = fopen(_PATH_RESCONF, "re")) != NULL) { struct stat sb; struct timespec now; if (statp->_u._ext.ext != NULL) { if (_fstat(fileno(fp), &sb) == 0) { statp->_u._ext.ext->conf_mtim = sb.st_mtim; if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) { statp->_u._ext.ext->conf_stat = now.tv_sec; } } } /* read the config file */ while (fgets(buf, sizeof(buf), fp) != NULL) { /* skip comments */ if (*buf == ';' || *buf == '#') continue; /* read default domain name */ if (MATCH(buf, "domain")) { if (haveenv) /*%< skip if have from environ */ continue; cp = buf + sizeof("domain") - 1; while (*cp == ' ' || *cp == '\t') cp++; if ((*cp == '\0') || (*cp == '\n')) continue; strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; if ((cp = strpbrk(statp->defdname, " \t\n")) != NULL) *cp = '\0'; havesearch = 0; continue; } /* set search list */ if (MATCH(buf, "search")) { if (haveenv) /*%< skip if have from environ */ continue; cp = buf + sizeof("search") - 1; while (*cp == ' ' || *cp == '\t') cp++; if ((*cp == '\0') || (*cp == '\n')) continue; strncpy(statp->defdname, cp, sizeof(statp->defdname) - 1); statp->defdname[sizeof(statp->defdname) - 1] = '\0'; if ((cp = strchr(statp->defdname, '\n')) != NULL) *cp = '\0'; /* * Set search list to be blank-separated strings * on rest of line. */ cp = statp->defdname; pp = statp->dnsrch; *pp++ = cp; for (n = 0; *cp && pp < statp->dnsrch + MAXDNSRCH; cp++) { if (*cp == ' ' || *cp == '\t') { *cp = 0; n = 1; } else if (n) { *pp++ = cp; n = 0; } } /* null terminate last domain if there are excess */ while (*cp != '\0' && *cp != ' ' && *cp != '\t') cp++; *cp = '\0'; *pp++ = NULL; havesearch = 1; continue; } /* read nameservers to query */ if (MATCH(buf, "nameserver") && nserv < maxns) { struct addrinfo hints, *ai; char sbuf[NI_MAXSERV]; const size_t minsiz = sizeof(statp->_u._ext.ext->nsaddrs[0]); cp = buf + sizeof("nameserver") - 1; while (*cp == ' ' || *cp == '\t') cp++; cp[strcspn(cp, ";# \t\n")] = '\0'; if ((*cp != '\0') && (*cp != '\n')) { memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; sprintf(sbuf, "%u", NAMESERVER_PORT); if (getaddrinfo(cp, sbuf, &hints, &ai) == 0) { if (ai->ai_addrlen <= minsiz) { if (statp->_u._ext.ext != NULL) { memcpy(&statp->_u._ext.ext->nsaddrs[nserv], ai->ai_addr, ai->ai_addrlen); } if (ai->ai_addrlen <= sizeof(statp->nsaddr_list[nserv])) { memcpy(&statp->nsaddr_list[nserv], ai->ai_addr, ai->ai_addrlen); } else statp->nsaddr_list[nserv].sin_family = 0; nserv++; } freeaddrinfo(ai); } } continue; } #ifdef RESOLVSORT if (MATCH(buf, "sortlist")) { struct in_addr a; struct in6_addr a6; int m, i; u_char *u; struct __res_state_ext *ext = statp->_u._ext.ext; cp = buf + sizeof("sortlist") - 1; while (nsort < MAXRESOLVSORT) { while (*cp == ' ' || *cp == '\t') cp++; if (*cp == '\0' || *cp == '\n' || *cp == ';') break; net = cp; while (*cp && !ISSORTMASK(*cp) && *cp != ';' && isascii(*cp) && !isspace((unsigned char)*cp)) cp++; n = *cp; *cp = 0; if (inet_aton(net, &a)) { statp->sort_list[nsort].addr = a; if (ISSORTMASK(n)) { *cp++ = n; net = cp; while (*cp && *cp != ';' && isascii(*cp) && !isspace((unsigned char)*cp)) cp++; n = *cp; *cp = 0; if (inet_aton(net, &a)) { statp->sort_list[nsort].mask = a.s_addr; } else { statp->sort_list[nsort].mask = net_mask(statp->sort_list[nsort].addr); } } else { statp->sort_list[nsort].mask = net_mask(statp->sort_list[nsort].addr); } ext->sort_list[nsort].af = AF_INET; ext->sort_list[nsort].addr.ina = statp->sort_list[nsort].addr; ext->sort_list[nsort].mask.ina.s_addr = statp->sort_list[nsort].mask; nsort++; } else if (inet_pton(AF_INET6, net, &a6) == 1) { ext->sort_list[nsort].af = AF_INET6; ext->sort_list[nsort].addr.in6a = a6; u = (u_char *)&ext->sort_list[nsort].mask.in6a; *cp++ = n; net = cp; while (*cp && *cp != ';' && isascii(*cp) && !isspace(*cp)) cp++; m = n; n = *cp; *cp = 0; switch (m) { case '/': m = atoi(net); break; case '&': if (inet_pton(AF_INET6, net, u) == 1) { m = -1; break; } /*FALLTHROUGH*/ default: m = sizeof(struct in6_addr) * CHAR_BIT; break; } if (m >= 0) { for (i = 0; i < sizeof(struct in6_addr); i++) { if (m <= 0) { *u = 0; } else { m -= CHAR_BIT; *u = (u_char)~0; if (m < 0) *u <<= -m; } u++; } } statp->sort_list[nsort].addr.s_addr = (u_int32_t)0xffffffff; statp->sort_list[nsort].mask = (u_int32_t)0xffffffff; nsort++; } *cp = n; } continue; } #endif if (MATCH(buf, "options")) { res_setoptions(statp, buf + sizeof("options") - 1, "conf"); continue; } } if (nserv > 0) statp->nscount = nserv; #ifdef RESOLVSORT statp->nsort = nsort; #endif (void) fclose(fp); } /* * Last chance to get a nameserver. This should not normally * be necessary */ #ifdef NO_RESOLV_CONF if(nserv == 0) nserv = get_nameservers(statp); #endif if (statp->defdname[0] == 0 && gethostname(buf, sizeof(statp->defdname) - 1) == 0 && (cp = strchr(buf, '.')) != NULL) strcpy(statp->defdname, cp + 1); /* find components of local domain that might be searched */ if (havesearch == 0) { pp = statp->dnsrch; *pp++ = statp->defdname; *pp = NULL; dots = 0; for (cp = statp->defdname; *cp; cp++) dots += (*cp == '.'); cp = statp->defdname; while (pp < statp->dnsrch + MAXDFLSRCH) { if (dots < LOCALDOMAINPARTS) break; cp = strchr(cp, '.') + 1; /*%< we know there is one */ *pp++ = cp; dots--; } *pp = NULL; #ifdef DEBUG if (statp->options & RES_DEBUG) { printf(";; res_init()... default dnsrch list:\n"); for (pp = statp->dnsrch; *pp; pp++) printf(";;\t%s\n", *pp); printf(";;\t..END..\n"); } #endif } if (issetugid()) statp->options |= RES_NOALIASES; else if ((cp = getenv("RES_OPTIONS")) != NULL) res_setoptions(statp, cp, "env"); statp->options |= RES_INIT; return (statp->res_h_errno); } static void res_setoptions(res_state statp, const char *options, const char *source) { const char *cp = options; int i; struct __res_state_ext *ext = statp->_u._ext.ext; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_setoptions(\"%s\", \"%s\")...\n", options, source); #endif while (*cp) { /* skip leading and inner runs of spaces */ while (*cp == ' ' || *cp == '\t') cp++; /* search for and process individual options */ if (!strncmp(cp, "ndots:", sizeof("ndots:") - 1)) { i = atoi(cp + sizeof("ndots:") - 1); if (i <= RES_MAXNDOTS) statp->ndots = i; else statp->ndots = RES_MAXNDOTS; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\tndots=%d\n", statp->ndots); #endif } else if (!strncmp(cp, "timeout:", sizeof("timeout:") - 1)) { i = atoi(cp + sizeof("timeout:") - 1); if (i <= RES_MAXRETRANS) statp->retrans = i; else statp->retrans = RES_MAXRETRANS; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\ttimeout=%d\n", statp->retrans); #endif #ifdef SOLARIS2 } else if (!strncmp(cp, "retrans:", sizeof("retrans:") - 1)) { /* * For backward compatibility, 'retrans' is * supported as an alias for 'timeout', though * without an imposed maximum. */ statp->retrans = atoi(cp + sizeof("retrans:") - 1); } else if (!strncmp(cp, "retry:", sizeof("retry:") - 1)){ /* * For backward compatibility, 'retry' is * supported as an alias for 'attempts', though * without an imposed maximum. */ statp->retry = atoi(cp + sizeof("retry:") - 1); #endif /* SOLARIS2 */ } else if (!strncmp(cp, "attempts:", sizeof("attempts:") - 1)){ i = atoi(cp + sizeof("attempts:") - 1); if (i <= RES_MAXRETRY) statp->retry = i; else statp->retry = RES_MAXRETRY; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";;\tattempts=%d\n", statp->retry); #endif } else if (!strncmp(cp, "debug", sizeof("debug") - 1)) { #ifdef DEBUG if (!(statp->options & RES_DEBUG)) { printf(";; res_setoptions(\"%s\", \"%s\")..\n", options, source); statp->options |= RES_DEBUG; } printf(";;\tdebug\n"); #endif } else if (!strncmp(cp, "no_tld_query", sizeof("no_tld_query") - 1) || !strncmp(cp, "no-tld-query", sizeof("no-tld-query") - 1)) { statp->options |= RES_NOTLDQUERY; } else if (!strncmp(cp, "inet6", sizeof("inet6") - 1)) { statp->options |= RES_USE_INET6; } else if (!strncmp(cp, "insecure1", sizeof("insecure1") - 1)) { statp->options |= RES_INSECURE1; } else if (!strncmp(cp, "insecure2", sizeof("insecure2") - 1)) { statp->options |= RES_INSECURE2; } else if (!strncmp(cp, "rotate", sizeof("rotate") - 1)) { statp->options |= RES_ROTATE; } else if (!strncmp(cp, "usevc", sizeof("usevc") - 1)) { statp->options |= RES_USEVC; } else if (!strncmp(cp, "no-check-names", sizeof("no-check-names") - 1)) { statp->options |= RES_NOCHECKNAME; } else if (!strncmp(cp, "reload-period:", sizeof("reload-period:") - 1)) { if (ext != NULL) { ext->reload_period = (u_short) atoi(cp + sizeof("reload-period:") - 1); } } #ifdef RES_USE_EDNS0 else if (!strncmp(cp, "edns0", sizeof("edns0") - 1)) { statp->options |= RES_USE_EDNS0; } #endif #ifndef _LIBC else if (!strncmp(cp, "dname", sizeof("dname") - 1)) { statp->options |= RES_USE_DNAME; } else if (!strncmp(cp, "nibble:", sizeof("nibble:") - 1)) { if (ext == NULL) goto skip; cp += sizeof("nibble:") - 1; i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix) - 1); strncpy(ext->nsuffix, cp, i); ext->nsuffix[i] = '\0'; } else if (!strncmp(cp, "nibble2:", sizeof("nibble2:") - 1)) { if (ext == NULL) goto skip; cp += sizeof("nibble2:") - 1; i = MIN(strcspn(cp, " \t"), sizeof(ext->nsuffix2) - 1); strncpy(ext->nsuffix2, cp, i); ext->nsuffix2[i] = '\0'; } else if (!strncmp(cp, "v6revmode:", sizeof("v6revmode:") - 1)) { cp += sizeof("v6revmode:") - 1; /* "nibble" and "bitstring" used to be valid */ if (!strncmp(cp, "single", sizeof("single") - 1)) { statp->options |= RES_NO_NIBBLE2; } else if (!strncmp(cp, "both", sizeof("both") - 1)) { statp->options &= ~RES_NO_NIBBLE2; } } #endif else { /* XXX - print a warning here? */ } #ifndef _LIBC skip: #endif /* skip to next run of spaces */ while (*cp && *cp != ' ' && *cp != '\t') cp++; } } #ifdef RESOLVSORT /* XXX - should really support CIDR which means explicit masks always. */ static u_int32_t net_mask(struct in_addr in) /*!< XXX - should really use system's version of this */ { u_int32_t i = ntohl(in.s_addr); if (IN_CLASSA(i)) return (htonl(IN_CLASSA_NET)); else if (IN_CLASSB(i)) return (htonl(IN_CLASSB_NET)); return (htonl(IN_CLASSC_NET)); } #endif static u_char srnd[16]; void res_rndinit(res_state statp) { struct timeval now; u_int32_t u32; u_int16_t u16; u_char *rnd = statp->_rnd == NULL ? srnd : statp->_rnd; gettimeofday(&now, NULL); u32 = now.tv_sec; memcpy(rnd, &u32, 4); u32 = now.tv_usec; memcpy(rnd + 4, &u32, 4); u32 += now.tv_sec; memcpy(rnd + 8, &u32, 4); u16 = getpid(); memcpy(rnd + 12, &u16, 2); } u_int res_nrandomid(res_state statp) { struct timeval now; u_int16_t u16; MD5_CTX ctx; u_char *rnd = statp->_rnd == NULL ? srnd : statp->_rnd; gettimeofday(&now, NULL); u16 = (u_int16_t) (now.tv_sec ^ now.tv_usec); memcpy(rnd + 14, &u16, 2); #ifndef HAVE_MD5 MD5_Init(&ctx); MD5_Update(&ctx, rnd, 16); MD5_Final(rnd, &ctx); #else MD5Init(&ctx); MD5Update(&ctx, rnd, 16); MD5Final(rnd, &ctx); #endif memcpy(&u16, rnd + 14, 2); return ((u_int) u16); } /*% * This routine is for closing the socket if a virtual circuit is used and * the program wants to close it. This provides support for endhostent() * which expects to close the socket. * * This routine is not expected to be user visible. */ void res_nclose(res_state statp) { int ns; if (statp->_vcsock >= 0) { (void) _close(statp->_vcsock); statp->_vcsock = -1; statp->_flags &= ~(RES_F_VC | RES_F_CONN); } for (ns = 0; ns < statp->_u._ext.nscount; ns++) { if (statp->_u._ext.nssocks[ns] != -1) { (void) _close(statp->_u._ext.nssocks[ns]); statp->_u._ext.nssocks[ns] = -1; } } } void res_ndestroy(res_state statp) { res_nclose(statp); if (statp->_u._ext.ext != NULL) { free(statp->_u._ext.ext); statp->_u._ext.ext = NULL; } if (statp->_rnd != NULL) { free(statp->_rnd); statp->_rnd = NULL; } statp->options &= ~RES_INIT; } #ifndef _LIBC const char * res_get_nibblesuffix(res_state statp) { if (statp->_u._ext.ext) return (statp->_u._ext.ext->nsuffix); return ("ip6.arpa"); } const char * res_get_nibblesuffix2(res_state statp) { if (statp->_u._ext.ext) return (statp->_u._ext.ext->nsuffix2); return ("ip6.int"); } #endif void res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt) { int i, nserv; size_t size; /* close open servers */ res_nclose(statp); /* cause rtt times to be forgotten */ statp->_u._ext.nscount = 0; nserv = 0; for (i = 0; i < cnt && nserv < MAXNS; i++) { switch (set->sin.sin_family) { case AF_INET: size = sizeof(set->sin); if (statp->_u._ext.ext) memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &set->sin, size); if (size <= sizeof(statp->nsaddr_list[nserv])) memcpy(&statp->nsaddr_list[nserv], &set->sin, size); else statp->nsaddr_list[nserv].sin_family = 0; nserv++; break; #ifdef HAS_INET6_STRUCTS case AF_INET6: size = sizeof(set->sin6); if (statp->_u._ext.ext) memcpy(&statp->_u._ext.ext->nsaddrs[nserv], &set->sin6, size); if (size <= sizeof(statp->nsaddr_list[nserv])) memcpy(&statp->nsaddr_list[nserv], &set->sin6, size); else statp->nsaddr_list[nserv].sin_family = 0; nserv++; break; #endif default: break; } set++; } statp->nscount = nserv; } int res_getservers(res_state statp, union res_sockaddr_union *set, int cnt) { int i; size_t size; u_int16_t family; for (i = 0; i < statp->nscount && i < cnt; i++) { if (statp->_u._ext.ext) family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family; else family = statp->nsaddr_list[i].sin_family; switch (family) { case AF_INET: size = sizeof(set->sin); if (statp->_u._ext.ext) memcpy(&set->sin, &statp->_u._ext.ext->nsaddrs[i], size); else memcpy(&set->sin, &statp->nsaddr_list[i], size); break; #ifdef HAS_INET6_STRUCTS case AF_INET6: size = sizeof(set->sin6); if (statp->_u._ext.ext) memcpy(&set->sin6, &statp->_u._ext.ext->nsaddrs[i], size); else memcpy(&set->sin6, &statp->nsaddr_list[i], size); break; #endif default: set->sin.sin_family = 0; break; } set++; } return (statp->nscount); } /*! \file */ diff --git a/lib/libc/resolv/res_query.c b/lib/libc/resolv/res_query.c index 8270e26ecdfb..10ac46ced8af 100644 --- a/lib/libc/resolv/res_query.c +++ b/lib/libc/resolv/res_query.c @@ -1,492 +1,490 @@ /*- * SPDX-License-Identifier: (ISC AND BSD-3-Clause) * * Portions Copyright (C) 2004, 2005, 2008 Internet Systems Consortium, Inc. ("ISC") * Portions Copyright (C) 1996-2001, 2003 Internet Software Consortium. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Portions Copyright (c) 1993 by Digital Equipment Corporation. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies, and that * the name of Digital Equipment Corporation not be used in advertising or * publicity pertaining to distribution of the document or software without * specific, written prior permission. * * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)res_query.c 8.1 (Berkeley) 6/4/93"; static const char rcsid[] = "$Id: res_query.c,v 1.11 2008/11/14 02:36:51 marka Exp $"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "port_before.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "port_after.h" /* Options. Leave them on. */ #ifndef DEBUG #define DEBUG #endif #if PACKETSZ > 1024 #define MAXPACKET PACKETSZ #else #define MAXPACKET 1024 #endif /*% * Formulate a normal query, send, and await answer. * Returned answer is placed in supplied buffer "answer". * Perform preliminary check of answer, returning success only * if no error is indicated and the answer count is nonzero. * Return the size of the response on success, -1 on error. * Error number is left in H_ERRNO. * * Caller must parse answer and determine whether it answers the question. */ int res_nquery(res_state statp, const char *name, /*%< domain name */ int class, int type, /*%< class and type of query */ u_char *answer, /*%< buffer to put answer */ int anslen) /*%< size of answer buffer */ { u_char buf[MAXPACKET]; HEADER *hp = (HEADER *) answer; u_int oflags; u_char *rdata; int n; oflags = statp->_flags; again: hp->rcode = NOERROR; /*%< default */ #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query(%s, %d, %d)\n", name, class, type); #endif n = res_nmkquery(statp, QUERY, name, class, type, NULL, 0, NULL, buf, sizeof(buf)); #ifdef RES_USE_EDNS0 if (n > 0 && (statp->_flags & RES_F_EDNS0ERR) == 0 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC|RES_NSID))) { n = res_nopt(statp, n, buf, sizeof(buf), anslen); if (n > 0 && (statp->options & RES_NSID) != 0U) { rdata = &buf[n]; n = res_nopt_rdata(statp, n, buf, sizeof(buf), rdata, NS_OPT_NSID, 0, NULL); } } #endif if (n <= 0) { #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query: mkquery failed\n"); #endif RES_SET_H_ERRNO(statp, NO_RECOVERY); return (n); } n = res_nsend(statp, buf, n, answer, anslen); if (n < 0) { #ifdef RES_USE_EDNS0 /* if the query choked with EDNS0, retry without EDNS0 */ if ((statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0U && ((oflags ^ statp->_flags) & RES_F_EDNS0ERR) != 0) { statp->_flags |= RES_F_EDNS0ERR; if (statp->options & RES_DEBUG) printf(";; res_nquery: retry without EDNS0\n"); goto again; } #endif #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_query: send error\n"); #endif RES_SET_H_ERRNO(statp, TRY_AGAIN); return (n); } if (hp->rcode != NOERROR || ntohs(hp->ancount) == 0) { #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; rcode = (%s), counts = an:%d ns:%d ar:%d\n", p_rcode(hp->rcode), ntohs(hp->ancount), ntohs(hp->nscount), ntohs(hp->arcount)); #endif switch (hp->rcode) { case NXDOMAIN: RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); break; case SERVFAIL: RES_SET_H_ERRNO(statp, TRY_AGAIN); break; case NOERROR: RES_SET_H_ERRNO(statp, NO_DATA); break; case FORMERR: case NOTIMP: case REFUSED: default: RES_SET_H_ERRNO(statp, NO_RECOVERY); break; } return (-1); } return (n); } /*% * Formulate a normal query, send, and retrieve answer in supplied buffer. * Return the size of the response on success, -1 on error. * If enabled, implement search rules until answer or unrecoverable failure * is detected. Error code, if any, is left in H_ERRNO. */ int res_nsearch(res_state statp, const char *name, /*%< domain name */ int class, int type, /*%< class and type of query */ u_char *answer, /*%< buffer to put answer */ int anslen) /*%< size of answer */ { const char *cp, * const *domain; HEADER *hp = (HEADER *) answer; char tmp[NS_MAXDNAME]; u_int dots; int trailing_dot, ret, saved_herrno; int got_nodata = 0, got_servfail = 0, root_on_list = 0; int tried_as_is = 0; int searched = 0; errno = 0; RES_SET_H_ERRNO(statp, HOST_NOT_FOUND); /*%< True if we never query. */ dots = 0; for (cp = name; *cp != '\0'; cp++) dots += (*cp == '.'); trailing_dot = 0; if (cp > name && *--cp == '.') trailing_dot++; /* If there aren't any dots, it could be a user-level alias. */ if (!dots && (cp = res_hostalias(statp, name, tmp, sizeof tmp))!= NULL) return (res_nquery(statp, cp, class, type, answer, anslen)); /* * If there are enough dots in the name, let's just give it a * try 'as is'. The threshold can be set with the "ndots" option. * Also, query 'as is', if there is a trailing dot in the name. */ saved_herrno = -1; if (dots >= statp->ndots || trailing_dot) { ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen); if (ret > 0 || trailing_dot) return (ret); if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(statp, TRY_AGAIN); return (-1); } switch (statp->res_h_errno) { case NO_DATA: case HOST_NOT_FOUND: break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) break; /* FALLTHROUGH */ default: return (-1); } saved_herrno = statp->res_h_errno; tried_as_is++; } /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ if ((!dots && (statp->options & RES_DEFNAMES) != 0U) || (dots && !trailing_dot && (statp->options & RES_DNSRCH) != 0U)) { int done = 0; for (domain = (const char * const *)statp->dnsrch; *domain && !done; domain++) { searched = 1; if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; if (root_on_list && tried_as_is) continue; ret = res_nquerydomain(statp, name, *domain, class, type, answer, anslen); if (ret > 0) return (ret); /* * If no server present, give up. * If name isn't found in this domain, * keep trying higher domains in the search list * (if that's enabled). * On a NO_DATA error, keep trying, otherwise * a wildcard entry of another type could keep us * from finding this entry higher in the domain. * If we get some other error (negative answer or * server failure), then stop searching up, * but try the input name below in case it's * fully-qualified. */ if (errno == ECONNREFUSED) { RES_SET_H_ERRNO(statp, TRY_AGAIN); return (-1); } switch (statp->res_h_errno) { case NO_DATA: got_nodata++; /* FALLTHROUGH */ case HOST_NOT_FOUND: /* keep trying */ break; case TRY_AGAIN: /* * This can occur due to a server failure * (that is, all listed servers have failed), * or all listed servers have timed out. * ((HEADER *)answer)->rcode may not be set * to SERVFAIL in the case of a timeout. * * Either way we must return TRY_AGAIN in * order to avoid non-deterministic * return codes. * For example, loaded name servers or races * against network startup/validation (dhcp, * ppp, etc) can cause the search to timeout * on one search element, e.g. 'fu.bar.com', * and return a definitive failure on the * next search element, e.g. 'fu.'. */ got_servfail++; if (hp->rcode == SERVFAIL) { /* try next search element, if any */ break; } /* FALLTHROUGH */ default: /* anything else implies that we're done */ done++; } /* if we got here for some reason other than DNSRCH, * we only wanted one iteration of the loop, so stop. */ if ((statp->options & RES_DNSRCH) == 0U) done++; } } switch (statp->res_h_errno) { case NO_DATA: case HOST_NOT_FOUND: break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) break; /* FALLTHROUGH */ default: goto giveup; } /* * If the query has not already been tried as is then try it * unless RES_NOTLDQUERY is set and there were no dots. */ if ((dots || !searched || (statp->options & RES_NOTLDQUERY) == 0U) && !(tried_as_is || root_on_list)) { ret = res_nquerydomain(statp, name, NULL, class, type, answer, anslen); if (ret > 0) return (ret); } /* if we got here, we didn't satisfy the search. * if we did an initial full query, return that query's H_ERRNO * (note that we wouldn't be here if that query had succeeded). * else if we ever got a nodata, send that back as the reason. * else send back meaningless H_ERRNO, that being the one from * the last DNSRCH we did. */ giveup: if (saved_herrno != -1) RES_SET_H_ERRNO(statp, saved_herrno); else if (got_nodata) RES_SET_H_ERRNO(statp, NO_DATA); else if (got_servfail) RES_SET_H_ERRNO(statp, TRY_AGAIN); return (-1); } /*% * Perform a call on res_query on the concatenation of name and domain, * removing a trailing dot from name if domain is NULL. */ int res_nquerydomain(res_state statp, const char *name, const char *domain, int class, int type, /*%< class and type of query */ u_char *answer, /*%< buffer to put answer */ int anslen) /*%< size of answer */ { char nbuf[MAXDNAME]; const char *longname = nbuf; int n, d; #ifdef DEBUG if (statp->options & RES_DEBUG) printf(";; res_nquerydomain(%s, %s, %d, %d)\n", name, domain?domain:"", class, type); #endif if (domain == NULL) { /* * Check for trailing '.'; * copy without '.' if present. */ n = strlen(name); if (n >= MAXDNAME) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } n--; if (n >= 0 && name[n] == '.') { strncpy(nbuf, name, n); nbuf[n] = '\0'; } else longname = name; } else { n = strlen(name); d = strlen(domain); if (n + d + 1 >= MAXDNAME) { RES_SET_H_ERRNO(statp, NO_RECOVERY); return (-1); } sprintf(nbuf, "%s.%s", name, domain); } return (res_nquery(statp, longname, class, type, answer, anslen)); } const char * res_hostalias(const res_state statp, const char *name, char *dst, size_t siz) { char *file, *cp1, *cp2; char buf[BUFSIZ]; FILE *fp; if (statp->options & RES_NOALIASES) return (NULL); - if (issetugid()) - return (NULL); - file = getenv("HOSTALIASES"); + file = secure_getenv("HOSTALIASES"); if (file == NULL || (fp = fopen(file, "re")) == NULL) return (NULL); setbuf(fp, NULL); buf[sizeof(buf) - 1] = '\0'; while (fgets(buf, sizeof(buf), fp)) { for (cp1 = buf; *cp1 && !isspace((unsigned char)*cp1); ++cp1) ; if (!*cp1) break; *cp1 = '\0'; if (ns_samename(buf, name) == 1) { while (isspace((unsigned char)*++cp1)) ; if (!*cp1) break; for (cp2 = cp1 + 1; *cp2 && !isspace((unsigned char)*cp2); ++cp2) ; *cp2 = '\0'; strncpy(dst, cp1, siz - 1); dst[siz - 1] = '\0'; fclose(fp); return (dst); } } fclose(fp); return (NULL); } /*! \file */ diff --git a/lib/libc/stdio/tempnam.c b/lib/libc/stdio/tempnam.c index 2d7bd90e08a4..4a720fd4c1cb 100644 --- a/lib/libc/stdio/tempnam.c +++ b/lib/libc/stdio/tempnam.c @@ -1,91 +1,91 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)tempnam.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include __warn_references(tempnam, "warning: tempnam() possibly used unsafely; consider using mkstemp()"); extern char *_mktemp(char *); char * tempnam(const char *dir, const char *pfx) { int sverrno; char *f, *name; if (!(name = malloc(MAXPATHLEN))) return(NULL); if (!pfx) pfx = "tmp."; - if (issetugid() == 0 && (f = getenv("TMPDIR"))) { + if ((f = secure_getenv("TMPDIR")) != NULL) { (void)snprintf(name, MAXPATHLEN, "%s%s%sXXXXXX", f, *(f + strlen(f) - 1) == '/'? "": "/", pfx); if ((f = _mktemp(name))) return(f); } if ((f = (char *)dir)) { (void)snprintf(name, MAXPATHLEN, "%s%s%sXXXXXX", f, *(f + strlen(f) - 1) == '/'? "": "/", pfx); if ((f = _mktemp(name))) return(f); } f = P_tmpdir; (void)snprintf(name, MAXPATHLEN, "%s%sXXXXXX", f, pfx); if ((f = _mktemp(name))) return(f); f = _PATH_TMP; (void)snprintf(name, MAXPATHLEN, "%s%sXXXXXX", f, pfx); if ((f = _mktemp(name))) return(f); sverrno = errno; free(name); errno = sverrno; return(NULL); } diff --git a/lib/libc/stdio/tmpfile.c b/lib/libc/stdio/tmpfile.c index e5ee1be2884e..aedaab6e1262 100644 --- a/lib/libc/stdio/tmpfile.c +++ b/lib/libc/stdio/tmpfile.c @@ -1,95 +1,93 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)tmpfile.c 8.1 (Berkeley) 6/4/93"; #endif /* LIBC_SCCS and not lint */ #include __FBSDID("$FreeBSD$"); #include "namespace.h" #include #include #include #include #include #include #include #include #include "un-namespace.h" #include "libc_private.h" FILE * tmpfile(void) { sigset_t set, oset; FILE *fp; int fd, sverrno; #define TRAILER "tmp.XXXXXX" char *buf; const char *tmpdir; - tmpdir = NULL; - if (issetugid() == 0) - tmpdir = getenv("TMPDIR"); + tmpdir = secure_getenv("TMPDIR"); if (tmpdir == NULL) tmpdir = _PATH_TMP; (void)asprintf(&buf, "%s%s%s", tmpdir, (tmpdir[strlen(tmpdir) - 1] == '/') ? "" : "/", TRAILER); if (buf == NULL) return (NULL); sigfillset(&set); (void)__libc_sigprocmask(SIG_BLOCK, &set, &oset); fd = mkstemp(buf); if (fd != -1) (void)unlink(buf); free(buf); (void)__libc_sigprocmask(SIG_SETMASK, &oset, NULL); if (fd == -1) return (NULL); if ((fp = fdopen(fd, "w+")) == NULL) { sverrno = errno; (void)_close(fd); errno = sverrno; return (NULL); } return (fp); }