Index: head/usr.sbin/ypserv/yp_access.c =================================================================== --- head/usr.sbin/ypserv/yp_access.c (revision 290918) +++ head/usr.sbin/ypserv/yp_access.c (revision 290919) @@ -1,334 +1,334 @@ /* * Copyright (c) 1995 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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 #include #include #include #include #include #include "yp_extern.h" #ifdef TCP_WRAPPER #include "tcpd.h" #endif extern int debug; static const char *yp_procs[] = { /* NIS v1 */ "ypoldproc_null", "ypoldproc_domain", "ypoldproc_domain_nonack", "ypoldproc_match", "ypoldproc_first", "ypoldproc_next", "ypoldproc_poll", "ypoldproc_push", "ypoldproc_get", "badproc1", /* placeholder */ "badproc2", /* placeholder */ "badproc3", /* placeholder */ /* NIS v2 */ "ypproc_null", "ypproc_domain", "ypproc_domain_nonack", "ypproc_match", "ypproc_first", "ypproc_next", "ypproc_xfr", "ypproc_clear", "ypproc_all", "ypproc_master", "ypproc_order", "ypproc_maplist" }; struct securenet { struct in_addr net; struct in_addr mask; struct securenet *next; }; static struct securenet *securenets; #define LINEBUFSZ 1024 #ifdef TCP_WRAPPER int hosts_ctl(char *, char *, char *, char *); #endif /* * Read /var/yp/securenets file and initialize the securenets * list. If the file doesn't exist, we set up a dummy entry that * allows all hosts to connect. */ void load_securenets(void) { FILE *fp; char path[MAXPATHLEN + 2]; char linebuf[1024 + 2]; struct securenet *tmp; /* * If securenets is not NULL, we are being called to reload * the list; free the existing list before re-reading the * securenets file. */ while (securenets) { tmp = securenets->next; free(securenets); securenets = tmp; } snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir); if ((fp = fopen(path, "r")) == NULL) { if (errno == ENOENT) { - securenets = (struct securenet *)malloc(sizeof(struct securenet)); + securenets = malloc(sizeof(struct securenet)); securenets->net.s_addr = INADDR_ANY; securenets->mask.s_addr = INADDR_ANY; securenets->next = NULL; return; } else { yp_error("fopen(%s) failed: %s", path, strerror(errno)); exit(1); } } securenets = NULL; while (fgets(linebuf, LINEBUFSZ, fp)) { char addr1[20], addr2[20]; if ((linebuf[0] == '#') || (strspn(linebuf, " \t\r\n") == strlen(linebuf))) continue; if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) { yp_error("badly formatted securenets entry: %s", linebuf); continue; } - tmp = (struct securenet *)malloc(sizeof(struct securenet)); + tmp = malloc(sizeof(struct securenet)); if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) { yp_error("badly formatted securenets entry: %s", addr1); free(tmp); continue; } if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) { yp_error("badly formatted securenets entry: %s", addr2); free(tmp); continue; } tmp->next = securenets; securenets = tmp; } fclose(fp); } /* * Access control functions. * * yp_access() checks the mapname and client host address and watches for * the following things: * * - If the client is referencing one of the master.passwd.* or shadow.* maps, * it must be using a privileged port to make its RPC to us. If it is, then * we can assume that the caller is root and allow the RPC to succeed. If it * isn't access is denied. * * - The client's IP address is checked against the securenets rules. * There are two kinds of securenets support: the built-in support, * which is very simple and depends on the presence of a * /var/yp/securenets file, and tcp-wrapper support, which requires * Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper * package does not ship with FreeBSD, we use the built-in support * by default. Users can recompile the server with the tcp-wrapper library * if they already have it installed and want to use hosts.allow and * hosts.deny to control access instead of having a separate securenets * file.) * * If no /var/yp/securenets file is present, the host access checks * are bypassed and all hosts are allowed to connect. * * The yp_validdomain() function checks the domain specified by the caller * to make sure it's actually served by this server. This is more a sanity * check than an a security check, but this seems to be the best place for * it. */ #ifdef DB_CACHE int yp_access(const char *map, const char *domain, const struct svc_req *rqstp) #else int yp_access(const char *map, const struct svc_req *rqstp) #endif { struct sockaddr_in *rqhost; int status_securenets = 0; #ifdef TCP_WRAPPER int status_tcpwrap; #endif static unsigned long oldaddr = 0; struct securenet *tmp; const char *yp_procedure = NULL; char procbuf[50]; if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) { snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu", (unsigned long)rqstp->rq_prog, (unsigned long)rqstp->rq_proc); yp_procedure = (char *)&procbuf; } else { yp_procedure = rqstp->rq_prog == YPPASSWDPROG ? "yppasswdprog_update" : yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))]; } rqhost = svc_getcaller(rqstp->rq_xprt); if (debug) { yp_error("procedure %s called from %s:%d", yp_procedure, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); if (map != NULL) yp_error("client is referencing map \"%s\".", map); } /* Check the map name if one was supplied. */ if (map != NULL) { if (strchr(map, '/')) { yp_error("embedded slash in map name \"%s\" -- \ possible spoof attempt from %s:%d", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); return(1); } #ifdef DB_CACHE if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) || #else if ((strstr(map, "master.passwd.") || strstr(map, "shadow.") || #endif (rqstp->rq_prog == YPPROG && rqstp->rq_proc == YPPROC_XFR) || (rqstp->rq_prog == YPXFRD_FREEBSD_PROG && rqstp->rq_proc == YPXFRD_GETMAP)) && ntohs(rqhost->sin_port) >= IPPORT_RESERVED) { yp_error("access to %s denied -- client %s:%d \ not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); return(1); } } #ifdef TCP_WRAPPER status_tcpwrap = hosts_ctl("ypserv", STRING_UNKNOWN, inet_ntoa(rqhost->sin_addr), ""); #endif tmp = securenets; while (tmp) { if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr) | tmp->net.s_addr) == rqhost->sin_addr.s_addr) { status_securenets = 1; break; } tmp = tmp->next; } #ifdef TCP_WRAPPER if (status_securenets == 0 || status_tcpwrap == 0) { #else if (status_securenets == 0) { #endif /* * One of the following two events occurred: * * (1) The /var/yp/securenets exists and the remote host does not * match any of the networks specified in it. * (2) The hosts.allow file has denied access and TCP_WRAPPER is * defined. * * In either case deny access. */ if (rqhost->sin_addr.s_addr != oldaddr) { yp_error("connect from %s:%d to procedure %s refused", inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port), yp_procedure); oldaddr = rqhost->sin_addr.s_addr; } return(1); } return(0); } int yp_validdomain(const char *domain) { struct stat statbuf; char dompath[MAXPATHLEN + 2]; if (domain == NULL || strstr(domain, "binding") || !strcmp(domain, ".") || !strcmp(domain, "..") || strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN) return(1); snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain); if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode)) return(1); return(0); } Index: head/usr.sbin/ypserv/yp_dblookup.c =================================================================== --- head/usr.sbin/ypserv/yp_dblookup.c (revision 290918) +++ head/usr.sbin/ypserv/yp_dblookup.c (revision 290919) @@ -1,733 +1,733 @@ /* * Copyright (c) 1995 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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 #include #include "yp_extern.h" int ypdb_debug = 0; enum ypstat yp_errno = YP_TRUE; #define PERM_SECURE (S_IRUSR|S_IWUSR) HASHINFO openinfo = { 4096, /* bsize */ 32, /* ffactor */ 256, /* nelem */ 2048 * 512, /* cachesize */ NULL, /* hash */ 0, /* lorder */ }; #ifdef DB_CACHE #include #ifndef MAXDBS #define MAXDBS 20 #endif static int numdbs = 0; struct dbent { DB *dbp; char *name; char *key; int size; int flags; }; static TAILQ_HEAD(circlehead, circleq_entry) qhead; struct circleq_entry { struct dbent *dbptr; TAILQ_ENTRY(circleq_entry) links; }; /* * Initialize the circular queue. */ void yp_init_dbs(void) { TAILQ_INIT(&qhead); return; } /* * Dynamically allocate an entry for the circular queue. * Return a NULL pointer on failure. */ static struct circleq_entry * yp_malloc_qent(void) { register struct circleq_entry *q; - q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry)); + q = malloc(sizeof(struct circleq_entry)); if (q == NULL) { yp_error("failed to malloc() circleq entry"); return(NULL); } bzero((char *)q, sizeof(struct circleq_entry)); - q->dbptr = (struct dbent *)malloc(sizeof(struct dbent)); + q->dbptr = malloc(sizeof(struct dbent)); if (q->dbptr == NULL) { yp_error("failed to malloc() circleq entry"); free(q); return(NULL); } bzero((char *)q->dbptr, sizeof(struct dbent)); return(q); } /* * Free a previously allocated circular queue * entry. */ static void yp_free_qent(struct circleq_entry *q) { /* * First, close the database. In theory, this is also * supposed to free the resources allocated by the DB * package, including the memory pointed to by q->dbptr->key. * This means we don't have to free q->dbptr->key here. */ if (q->dbptr->dbp) { (void)(q->dbptr->dbp->close)(q->dbptr->dbp); q->dbptr->dbp = NULL; } /* * Then free the database name, which was strdup()'ed. */ free(q->dbptr->name); /* * Free the rest of the dbent struct. */ free(q->dbptr); q->dbptr = NULL; /* * Free the circleq struct. */ free(q); q = NULL; return; } /* * Zorch a single entry in the dbent queue and release * all its resources. (This always removes the last entry * in the queue.) */ static void yp_flush(void) { register struct circleq_entry *qptr; qptr = TAILQ_LAST(&qhead, circlehead); TAILQ_REMOVE(&qhead, qptr, links); yp_free_qent(qptr); numdbs--; return; } /* * Close all databases, erase all database names and empty the queue. */ void yp_flush_all(void) { register struct circleq_entry *qptr; while (!TAILQ_EMPTY(&qhead)) { qptr = TAILQ_FIRST(&qhead); /* save this */ TAILQ_REMOVE(&qhead, qptr, links); yp_free_qent(qptr); } numdbs = 0; return; } static char *inter_string = "YP_INTERDOMAIN"; static char *secure_string = "YP_SECURE"; static int inter_sz = sizeof("YP_INTERDOMAIN") - 1; static int secure_sz = sizeof("YP_SECURE") - 1; static int yp_setflags(DB *dbp) { DBT key = { NULL, 0 }, data = { NULL, 0 }; int flags = 0; key.data = inter_string; key.size = inter_sz; if (!(dbp->get)(dbp, &key, &data, 0)) flags |= YP_INTERDOMAIN; key.data = secure_string; key.size = secure_sz; if (!(dbp->get)(dbp, &key, &data, 0)) flags |= YP_SECURE; return(flags); } int yp_testflag(char *map, char *domain, int flag) { char buf[MAXPATHLEN + 2]; register struct circleq_entry *qptr; if (map == NULL || domain == NULL) return(0); strcpy(buf, domain); strcat(buf, "/"); strcat(buf, map); TAILQ_FOREACH(qptr, &qhead, links) { if (!strcmp(qptr->dbptr->name, buf)) { if (qptr->dbptr->flags & flag) return(1); else return(0); } } if (yp_open_db_cache(domain, map, NULL, 0) == NULL) return(0); if (TAILQ_FIRST(&qhead)->dbptr->flags & flag) return(1); return(0); } /* * Add a DB handle and database name to the cache. We only maintain * fixed number of entries in the cache, so if we're asked to store * a new entry when all our slots are already filled, we have to kick * out the entry in the last slot to make room. */ static int yp_cache_db(DB *dbp, char *name, int size) { register struct circleq_entry *qptr; if (numdbs == MAXDBS) { if (ypdb_debug) yp_error("queue overflow -- releasing last slot"); yp_flush(); } /* * Allocate a new queue entry. */ if ((qptr = yp_malloc_qent()) == NULL) { yp_error("failed to allocate a new cache entry"); return(1); } qptr->dbptr->dbp = dbp; qptr->dbptr->name = strdup(name); qptr->dbptr->size = size; qptr->dbptr->key = NULL; qptr->dbptr->flags = yp_setflags(dbp); TAILQ_INSERT_HEAD(&qhead, qptr, links); numdbs++; return(0); } /* * Search the list for a database matching 'name.' If we find it, * move it to the head of the list and return its DB handle. If * not, just fail: yp_open_db_cache() will subsequently try to open * the database itself and call yp_cache_db() to add it to the * list. * * The search works like this: * * - The caller specifies the name of a database to locate. We try to * find an entry in our queue with a matching name. * * - If the caller doesn't specify a key or size, we assume that the * first entry that we encounter with a matching name is returned. * This will result in matches regardless of the key/size values * stored in the queue entry. * * - If the caller also specifies a key and length, we check to see * if the key and length saved in the queue entry also matches. * This lets us return a DB handle that's already positioned at the * correct location within a database. * * - Once we have a match, it gets migrated to the top of the queue * so that it will be easier to find if another request for * the same database comes in later. */ static DB * yp_find_db(const char *name, const char *key, int size) { register struct circleq_entry *qptr; TAILQ_FOREACH(qptr, &qhead, links) { if (!strcmp(qptr->dbptr->name, name)) { if (size) { if (size != qptr->dbptr->size || strncmp(qptr->dbptr->key, key, size)) continue; } else { if (qptr->dbptr->size) continue; } if (qptr != TAILQ_FIRST(&qhead)) { TAILQ_REMOVE(&qhead, qptr, links); TAILQ_INSERT_HEAD(&qhead, qptr, links); } return(qptr->dbptr->dbp); } } return(NULL); } /* * Open a DB database and cache the handle for later use. We first * check the cache to see if the required database is already open. * If so, we fetch the handle from the cache. If not, we try to open * the database and save the handle in the cache for later use. */ DB * yp_open_db_cache(const char *domain, const char *map, const char *key, const int size) { DB *dbp = NULL; char buf[MAXPATHLEN + 2]; /* snprintf(buf, sizeof(buf), "%s/%s", domain, map); */ yp_errno = YP_TRUE; strcpy(buf, domain); strcat(buf, "/"); strcat(buf, map); if ((dbp = yp_find_db(buf, key, size)) != NULL) { return(dbp); } else { if ((dbp = yp_open_db(domain, map)) != NULL) { if (yp_cache_db(dbp, buf, size)) { (void)(dbp->close)(dbp); yp_errno = YP_YPERR; return(NULL); } } } return (dbp); } #endif /* * Open a DB database. */ DB * yp_open_db(const char *domain, const char *map) { DB *dbp = NULL; char buf[MAXPATHLEN + 2]; yp_errno = YP_TRUE; if (map[0] == '.' || strchr(map, '/')) { yp_errno = YP_BADARGS; return (NULL); } #ifdef DB_CACHE if (yp_validdomain(domain)) { yp_errno = YP_NODOM; return(NULL); } #endif snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); #ifdef DB_CACHE again: #endif dbp = dbopen(buf, O_RDONLY, PERM_SECURE, DB_HASH, NULL); if (dbp == NULL) { switch (errno) { #ifdef DB_CACHE case ENFILE: /* * We ran out of file descriptors. Nuke an * open one and try again. */ yp_error("ran out of file descriptors"); yp_flush(); goto again; break; #endif case ENOENT: yp_errno = YP_NOMAP; break; case EFTYPE: yp_errno = YP_BADDB; break; default: yp_errno = YP_YPERR; break; } } return (dbp); } /* * Database access routines. * * - yp_get_record(): retrieve an arbitrary key/data pair given one key * to match against. * * - yp_first_record(): retrieve first key/data base in a database. * * - yp_next_record(): retrieve key/data pair that sequentially follows * the supplied key value in the database. */ #ifdef DB_CACHE int yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow) #else int yp_get_record(const char *domain, const char *map, const DBT *key, DBT *data, int allow) #endif { #ifndef DB_CACHE DB *dbp; #endif int rval = 0; #ifndef DB_CACHE static unsigned char buf[YPMAXRECORD]; #endif if (ypdb_debug) yp_error("looking up key [%.*s]", (int)key->size, (char *)key->data); /* * Avoid passing back magic "YP_*" entries unless * the caller specifically requested them by setting * the 'allow' flag. */ if (!allow && !strncmp(key->data, "YP_", 3)) return(YP_NOKEY); #ifndef DB_CACHE if ((dbp = yp_open_db(domain, map)) == NULL) { return(yp_errno); } #endif if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->size = 0; #else (void)(dbp->close)(dbp); #endif if (rval == 1) return(YP_NOKEY); else return(YP_BADDB); } if (ypdb_debug) yp_error("result of lookup: key: [%.*s] data: [%.*s]", (int)key->size, (char *)key->data, (int)data->size, (char *)data->data); #ifdef DB_CACHE if (TAILQ_FIRST(&qhead)->dbptr->size) { TAILQ_FIRST(&qhead)->dbptr->key = ""; TAILQ_FIRST(&qhead)->dbptr->size = 0; } #else bcopy(data->data, &buf, data->size); data->data = &buf; (void)(dbp->close)(dbp); #endif return(YP_TRUE); } int yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow) { int rval; #ifndef DB_CACHE static unsigned char buf[YPMAXRECORD]; #endif if (ypdb_debug) yp_error("retrieving first key in map"); if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->size = 0; #endif if (rval == 1) return(YP_NOKEY); else return(YP_BADDB); } /* Avoid passing back magic "YP_*" records. */ while (!strncmp(key->data, "YP_", 3) && !allow) { if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->size = 0; #endif if (rval == 1) return(YP_NOKEY); else return(YP_BADDB); } } if (ypdb_debug) yp_error("result of lookup: key: [%.*s] data: [%.*s]", (int)key->size, (char *)key->data, (int)data->size, (char *)data->data); #ifdef DB_CACHE if (TAILQ_FIRST(&qhead)->dbptr->size) { TAILQ_FIRST(&qhead)->dbptr->key = key->data; TAILQ_FIRST(&qhead)->dbptr->size = key->size; } #else bcopy(data->data, &buf, data->size); data->data = &buf; #endif return(YP_TRUE); } int yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow) { static DBT lkey = { NULL, 0 }; static DBT ldata = { NULL, 0 }; int rval; #ifndef DB_CACHE static unsigned char keybuf[YPMAXRECORD]; static unsigned char datbuf[YPMAXRECORD]; #endif if (key == NULL || !key->size || key->data == NULL) { rval = yp_first_record(dbp,key,data,allow); if (rval == YP_NOKEY) return(YP_NOMORE); else { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->key = key->data; TAILQ_FIRST(&qhead)->dbptr->size = key->size; #endif return(rval); } } if (ypdb_debug) yp_error("retrieving next key, previous was: [%.*s]", (int)key->size, (char *)key->data); if (!all) { #ifdef DB_CACHE if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) { #endif (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); while (key->size != lkey.size || strncmp(key->data, lkey.data, (int)key->size)) if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->size = 0; #endif return(YP_NOKEY); } #ifdef DB_CACHE } #endif } if ((dbp->seq)(dbp,key,data,R_NEXT)) { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->size = 0; #endif return(YP_NOMORE); } /* Avoid passing back magic "YP_*" records. */ while (!strncmp(key->data, "YP_", 3) && !allow) if ((dbp->seq)(dbp,key,data,R_NEXT)) { #ifdef DB_CACHE TAILQ_FIRST(&qhead)->dbptr->size = 0; #endif return(YP_NOMORE); } if (ypdb_debug) yp_error("result of lookup: key: [%.*s] data: [%.*s]", (int)key->size, (char *)key->data, (int)data->size, (char *)data->data); #ifdef DB_CACHE if (TAILQ_FIRST(&qhead)->dbptr->size) { TAILQ_FIRST(&qhead)->dbptr->key = key->data; TAILQ_FIRST(&qhead)->dbptr->size = key->size; } #else bcopy(key->data, &keybuf, key->size); lkey.data = &keybuf; lkey.size = key->size; bcopy(data->data, &datbuf, data->size); data->data = &datbuf; #endif return(YP_TRUE); } #ifdef DB_CACHE /* * Database glue functions. */ static DB *yp_currmap_db = NULL; static int yp_allow_db = 0; ypstat yp_select_map(char *map, char *domain, keydat *key, int allow) { if (key == NULL) yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0); else yp_currmap_db = yp_open_db_cache(domain, map, key->keydat_val, key->keydat_len); yp_allow_db = allow; return(yp_errno); } ypstat yp_getbykey(keydat *key, valdat *val) { DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; ypstat rval; db_key.data = key->keydat_val; db_key.size = key->keydat_len; rval = yp_get_record(yp_currmap_db, &db_key, &db_val, yp_allow_db); if (rval == YP_TRUE) { val->valdat_val = db_val.data; val->valdat_len = db_val.size; } return(rval); } ypstat yp_firstbykey(keydat *key, valdat *val) { DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; ypstat rval; rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db); if (rval == YP_TRUE) { key->keydat_val = db_key.data; key->keydat_len = db_key.size; val->valdat_val = db_val.data; val->valdat_len = db_val.size; } return(rval); } ypstat yp_nextbykey(keydat *key, valdat *val) { DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; ypstat rval; db_key.data = key->keydat_val; db_key.size = key->keydat_len; rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db); if (rval == YP_TRUE) { key->keydat_val = db_key.data; key->keydat_len = db_key.size; val->valdat_val = db_val.data; val->valdat_len = db_val.size; } return(rval); } #endif Index: head/usr.sbin/ypserv/yp_dnslookup.c =================================================================== --- head/usr.sbin/ypserv/yp_dnslookup.c (revision 290918) +++ head/usr.sbin/ypserv/yp_dnslookup.c (revision 290919) @@ -1,551 +1,551 @@ /* * Copyright (c) 1995, 1996 * Bill Paul . 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bill Paul. * 4. 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 Bill Paul 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 Bill Paul 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$"); /* * Do standard and reverse DNS lookups using the resolver library. * Take care of all the dirty work here so the main program only has to * pass us a pointer to an array of characters. * * We have to use direct resolver calls here otherwise the YP server * could end up looping by calling itself over and over again until * it disappeared up its own belly button. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "yp_extern.h" static char * parse(struct hostent *hp) { static char result[MAXHOSTNAMELEN * 2]; int i; size_t len; char addr[46]; if (hp == NULL) return(NULL); if (inet_ntop(hp->h_addrtype, hp->h_addr, addr, sizeof(addr)) == NULL) return(NULL); len = strlen(addr) + 1 + strlen(hp->h_name); for (i = 0; hp->h_aliases[i]; i++) len += strlen(hp->h_aliases[i]) + 1; len++; if (len > sizeof(result)) return(NULL); bzero(result, sizeof(result)); snprintf(result, sizeof(result), "%s %s", addr, hp->h_name); for (i = 0; hp->h_aliases[i]; i++) { strcat(result, " "); strcat(result, hp->h_aliases[i]); } return ((char *)&result); } #define MAXPACKET (64*1024) #define DEF_TTL 50 #define BY_DNS_ID 1 #define BY_RPC_XID 2 extern struct hostent *__dns_getanswer(char *, int, char *, int); static TAILQ_HEAD(dns_qhead, circleq_dnsentry) qhead; struct circleq_dnsentry { SVCXPRT *xprt; unsigned long xid; struct sockaddr_in client_addr; unsigned long ypvers; unsigned long id; unsigned long ttl; unsigned long type; unsigned short prot_type; char **domain; char *name; int addrtype; int addrlen; uint32_t addr[4]; /* IPv4 or IPv6 */ TAILQ_ENTRY(circleq_dnsentry) links; }; static int pending = 0; int yp_init_resolver(void) { TAILQ_INIT(&qhead); if (!(_res.options & RES_INIT) && res_init() == -1) { yp_error("res_init failed"); return(1); } if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { yp_error("couldn't create socket"); return(1); } if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) { yp_error("couldn't make resolver socket non-blocking"); return(1); } return(0); } static struct circleq_dnsentry *yp_malloc_dnsent(void) { register struct circleq_dnsentry *q; - q = (struct circleq_dnsentry *)malloc(sizeof(struct circleq_dnsentry)); + q = malloc(sizeof(struct circleq_dnsentry)); if (q == NULL) { yp_error("failed to malloc() circleq dns entry"); return(NULL); } return(q); } /* * Transmit a query. */ static unsigned long yp_send_dns_query(char *name, int type) { char buf[MAXPACKET]; int n; HEADER *hptr; int ns; int rval; unsigned long id; bzero(buf, sizeof(buf)); n = res_mkquery(QUERY,name,C_IN,type,NULL,0,NULL,buf,sizeof(buf)); if (n <= 0) { yp_error("res_mkquery failed for %s type %d", name, type); return(0); } hptr = (HEADER *)&buf; id = ntohs(hptr->id); for (ns = 0; ns < _res.nscount; ns++) { rval = sendto(resfd, buf, n, 0, (struct sockaddr *)&_res.nsaddr_list[ns], sizeof(struct sockaddr)); if (rval == -1) { yp_error("sendto failed"); return(0); } } return(id); } static struct circleq_dnsentry * yp_find_dnsqent(unsigned long id, int type) { register struct circleq_dnsentry *q; TAILQ_FOREACH(q, &qhead, links) { switch (type) { case BY_RPC_XID: if (id == q->xid) return(q); break; case BY_DNS_ID: default: if (id == q->id) return(q); break; } } return (NULL); } static void yp_send_dns_reply(struct circleq_dnsentry *q, char *buf) { ypresponse result_v1; ypresp_val result_v2; unsigned long xid; struct sockaddr_in client_addr; xdrproc_t xdrfunc; char *result; /* * Set up correct reply struct and * XDR filter depending on ypvers. */ switch (q->ypvers) { case YPVERS: bzero((char *)&result_v2, sizeof(result_v2)); if (buf == NULL) result_v2.stat = YP_NOKEY; else { result_v2.val.valdat_len = strlen(buf); result_v2.val.valdat_val = buf; result_v2.stat = YP_TRUE; } result = (char *)&result_v2; xdrfunc = (xdrproc_t)xdr_ypresp_val; break; case YPOLDVERS: /* * The odds are we will _never_ execute this * particular code, but we include it anyway * for the sake of completeness. */ bzero((char *)&result_v1, sizeof(result_v1)); result_v1.yp_resptype = YPRESP_VAL; #define YPVAL ypresponse_u.yp_resp_valtype if (buf == NULL) result_v1.YPVAL.stat = YP_NOKEY; else { result_v1.YPVAL.val.valdat_len = strlen(buf); result_v1.YPVAL.val.valdat_val = buf; result_v1.YPVAL.stat = YP_TRUE; } result = (char *)&result_v1; xdrfunc = (xdrproc_t)xdr_ypresponse; break; default: yp_error("bad YP program version (%lu)!", q->ypvers); return; break; } if (debug) yp_error("sending dns reply to %s (%lu)", inet_ntoa(q->client_addr.sin_addr), q->id); /* * XXX This is disgusting. There's basically one transport * handle for UDP, but we're holding off on replying to a * client until we're ready, by which time we may have received * several other queries from other clients with different * transaction IDs. So to make the delayed response thing work, * we have to save the transaction ID and client address of * each request, then jam them into the transport handle when * we're ready to send a reply. Then after we've send the reply, * we put the old transaction ID and remote address back the * way we found 'em. This is _INCREDIBLY_ non-portable; it's * not even supported by the RPC library. */ /* * XXX Don't frob the transaction ID for TCP handles. */ if (q->prot_type == SOCK_DGRAM) xid = svcudp_set_xid(q->xprt, q->xid); client_addr = q->xprt->xp_raddr; q->xprt->xp_raddr = q->client_addr; if (!svc_sendreply(q->xprt, xdrfunc, result)) yp_error("svc_sendreply failed"); /* * Now that we sent the reply, * put the handle back the way it was. */ if (q->prot_type == SOCK_DGRAM) svcudp_set_xid(q->xprt, xid); q->xprt->xp_raddr = client_addr; return; } /* * Decrement TTL on all queue entries, possibly nuking * any that have been around too long without being serviced. */ void yp_prune_dnsq(void) { register struct circleq_dnsentry *q, *n; q = TAILQ_FIRST(&qhead); while (q != NULL) { q->ttl--; n = TAILQ_NEXT(q, links); if (!q->ttl) { TAILQ_REMOVE(&qhead, q, links); free(q->name); free(q); pending--; } q = n; } if (pending < 0) pending = 0; return; } /* * Data is pending on the DNS socket; check for valid replies * to our queries and dispatch them to waiting clients. */ void yp_run_dnsq(void) { register struct circleq_dnsentry *q; char buf[sizeof(HEADER) + MAXPACKET]; struct sockaddr_in sin; socklen_t len; int rval; HEADER *hptr; struct hostent *hent; if (debug) yp_error("running dns queue"); bzero(buf, sizeof(buf)); len = sizeof(struct sockaddr_in); rval = recvfrom(resfd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &len); if (rval == -1) { yp_error("recvfrom failed: %s", strerror(errno)); return; } /* * We may have data left in the socket that represents * replies to earlier queries that we don't care about * anymore. If there are no lookups pending or the packet * ID doesn't match any of the queue IDs, just drop it * on the floor. */ hptr = (HEADER *)&buf; if (!pending || (q = yp_find_dnsqent(ntohs(hptr->id), BY_DNS_ID)) == NULL) { /* ignore */ return; } if (debug) yp_error("got dns reply from %s", inet_ntoa(sin.sin_addr)); hent = __dns_getanswer(buf, rval, q->name, q->type); if (hent != NULL) { if (q->type == T_PTR) { hent->h_addr = (char *)q->addr; hent->h_addrtype = q->addrtype; hent->h_length = q->addrlen; } } /* Got an answer ready for a client -- send it off. */ yp_send_dns_reply(q, parse(hent)); pending--; TAILQ_REMOVE(&qhead, q, links); free(q->name); free(q); /* Decrement TTLs on other entries while we're here. */ yp_prune_dnsq(); return; } /* * Queue and transmit an asynchronous DNS hostname lookup. */ ypstat yp_async_lookup_name(struct svc_req *rqstp, char *name, int af) { register struct circleq_dnsentry *q; socklen_t len; int type; /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ type = -1; len = sizeof(type); if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1) { yp_error("getsockopt failed: %s", strerror(errno)); return(YP_YPERR); } /* Avoid transmitting dupe requests. */ if (type == SOCK_DGRAM && yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL) return(YP_TRUE); if ((q = yp_malloc_dnsent()) == NULL) return(YP_YPERR); q->type = (af == AF_INET) ? T_A : T_AAAA; q->ttl = DEF_TTL; q->xprt = rqstp->rq_xprt; q->ypvers = rqstp->rq_vers; q->prot_type = type; if (q->prot_type == SOCK_DGRAM) q->xid = svcudp_get_xid(q->xprt); q->client_addr = q->xprt->xp_raddr; q->domain = _res.dnsrch; q->id = yp_send_dns_query(name, q->type); if (q->id == 0) { yp_error("DNS query failed"); free(q); return(YP_YPERR); } q->name = strdup(name); TAILQ_INSERT_HEAD(&qhead, q, links); pending++; if (debug) yp_error("queueing async DNS name lookup (%lu)", q->id); yp_prune_dnsq(); return(YP_TRUE); } /* * Queue and transmit an asynchronous DNS IP address lookup. */ ypstat yp_async_lookup_addr(struct svc_req *rqstp, char *addr, int af) { register struct circleq_dnsentry *q; char buf[MAXHOSTNAMELEN], *qp; uint32_t abuf[4]; /* IPv4 or IPv6 */ u_char *uaddr = (u_char *)abuf; socklen_t len; int type, n; /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */ type = -1; len = sizeof(type); if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET, SO_TYPE, &type, &len) == -1) { yp_error("getsockopt failed: %s", strerror(errno)); return(YP_YPERR); } /* Avoid transmitting dupe requests. */ if (type == SOCK_DGRAM && yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL) return(YP_TRUE); if ((q = yp_malloc_dnsent()) == NULL) return(YP_YPERR); switch (af) { case AF_INET: if (inet_aton(addr, (struct in_addr *)uaddr) != 1) return(YP_NOKEY); snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa", (uaddr[3] & 0xff), (uaddr[2] & 0xff), (uaddr[1] & 0xff), (uaddr[0] & 0xff)); len = INADDRSZ; break; case AF_INET6: if (inet_pton(af, addr, uaddr) != 1) return(YP_NOKEY); qp = buf; for (n = IN6ADDRSZ - 1; n >= 0; n--) { qp += (size_t)sprintf(qp, "%x.%x.", uaddr[n] & 0xf, (uaddr[n] >> 4) & 0xf); } strlcat(buf, "ip6.arpa", sizeof(buf)); len = IN6ADDRSZ; break; default: return(YP_YPERR); } if (debug) yp_error("DNS address is: %s", buf); q->type = T_PTR; q->ttl = DEF_TTL; q->xprt = rqstp->rq_xprt; q->ypvers = rqstp->rq_vers; q->domain = NULL; q->prot_type = type; if (q->prot_type == SOCK_DGRAM) q->xid = svcudp_get_xid(q->xprt); q->client_addr = q->xprt->xp_raddr; q->id = yp_send_dns_query(buf, q->type); if (q->id == 0) { yp_error("DNS query failed"); free(q); return(YP_YPERR); } memcpy(q->addr, uaddr, len); q->addrlen = len; q->addrtype = af; q->name = strdup(buf); TAILQ_INSERT_HEAD(&qhead, q, links); pending++; if (debug) yp_error("queueing async DNS address lookup (%lu)", q->id); yp_prune_dnsq(); return(YP_TRUE); }