xref: /freebsd/usr.sbin/ypserv/yp_dblookup.c (revision 3e0f6b97b257a96f7275e4442204263e44b16686)
1 /*
2  * Copyright (c) 1995
3  *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by Bill Paul.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *	$FreeBSD$
33  *
34  */
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <fcntl.h>
38 #include <string.h>
39 #include <limits.h>
40 #include <unistd.h>
41 #include <db.h>
42 #include <sys/stat.h>
43 #include <sys/param.h>
44 #include <errno.h>
45 #include <paths.h>
46 #include <rpcsvc/yp.h>
47 #include "yp_extern.h"
48 
49 #ifndef lint
50 static const char rcsid[] = "$FreeBSD$";
51 #endif
52 
53 int ypdb_debug = 0;
54 enum ypstat yp_errno = YP_TRUE;
55 
56 #define PERM_SECURE (S_IRUSR|S_IWUSR)
57 HASHINFO openinfo = {
58 	4096,		/* bsize */
59 	32,		/* ffactor */
60 	256,		/* nelem */
61 	2048 * 512, 	/* cachesize */
62 	NULL,		/* hash */
63 	0,		/* lorder */
64 };
65 
66 #ifdef DB_CACHE
67 #include <sys/queue.h>
68 
69 #ifndef MAXDBS
70 #define MAXDBS 20
71 #endif
72 
73 static int numdbs = 0;
74 
75 struct dbent {
76 	DB *dbp;
77 	char *name;
78 	char *key;
79 	int size;
80 	int flags;
81 };
82 
83 static CIRCLEQ_HEAD(circlehead, circleq_entry) qhead;
84 
85 struct circleq_entry {
86 	struct dbent *dbptr;
87 	CIRCLEQ_ENTRY(circleq_entry) links;
88 };
89 
90 /*
91  * Initialize the circular queue.
92  */
93 void yp_init_dbs()
94 {
95 	CIRCLEQ_INIT(&qhead);
96 	return;
97 }
98 
99 /*
100  * Dynamically allocate an entry for the circular queue.
101  * Return a NULL pointer on failure.
102  */
103 static struct circleq_entry *yp_malloc_qent()
104 {
105 	register struct circleq_entry *q;
106 
107 	q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry));
108 	if (q == NULL) {
109 		yp_error("failed to malloc() circleq entry: %s",
110 							strerror(errno));
111 		return(NULL);
112 	}
113 	bzero((char *)q, sizeof(struct circleq_entry));
114 	q->dbptr = (struct dbent *)malloc(sizeof(struct dbent));
115 	if (q->dbptr == NULL) {
116 		yp_error("failed to malloc() circleq entry: %s",
117 							strerror(errno));
118 		free(q);
119 		return(NULL);
120 	}
121 	bzero((char *)q->dbptr, sizeof(struct dbent));
122 
123 	return(q);
124 }
125 
126 /*
127  * Free a previously allocated circular queue
128  * entry.
129  */
130 static void yp_free_qent(q)
131 	struct circleq_entry *q;
132 {
133 	/*
134 	 * First, close the database. In theory, this is also
135 	 * supposed to free the resources allocated by the DB
136 	 * package, including the memory pointed to by q->dbptr->key.
137 	 * This means we don't have to free q->dbptr->key here.
138 	 */
139 	if (q->dbptr->dbp) {
140 		(void)(q->dbptr->dbp->close)(q->dbptr->dbp);
141 		q->dbptr->dbp = NULL;
142 	}
143 	/*
144 	 * Then free the database name, which was strdup()'ed.
145 	 */
146 	free(q->dbptr->name);
147 
148 	/*
149 	 * Free the rest of the dbent struct.
150 	 */
151 	free(q->dbptr);
152 	q->dbptr = NULL;
153 
154 	/*
155 	 * Free the circleq struct.
156 	 */
157 	free(q);
158 	q = NULL;
159 
160 	return;
161 }
162 
163 /*
164  * Zorch a single entry in the dbent queue and release
165  * all its resources. (This always removes the last entry
166  * in the queue.)
167  */
168 static void yp_flush()
169 {
170 	register struct circleq_entry *qptr;
171 
172 	qptr = qhead.cqh_last;
173 	CIRCLEQ_REMOVE(&qhead, qptr, links);
174 	yp_free_qent(qptr);
175 	numdbs--;
176 
177 	return;
178 }
179 
180 /*
181  * Close all databases, erase all database names and empty the queue.
182  */
183 void yp_flush_all()
184 {
185 	register struct circleq_entry *qptr;
186 
187 	while(qhead.cqh_first != (void *)&qhead) {
188 		qptr = qhead.cqh_first; /* save this */
189 		CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, links);
190 		yp_free_qent(qptr);
191 	}
192 	numdbs = 0;
193 
194 	return;
195 }
196 
197 static char *inter_string = "YP_INTERDOMAIN";
198 static char *secure_string = "YP_SECURE";
199 static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
200 static int secure_sz = sizeof("YP_SECURE") - 1;
201 
202 static int yp_setflags(dbp)
203 	DB *dbp;
204 {
205 	DBT key = { NULL, 0 }, data = { NULL, 0 };
206 	int flags = 0;
207 
208 	key.data = inter_string;
209 	key.size = inter_sz;
210 
211 	if (!(dbp->get)(dbp, &key, &data, 0))
212 		flags |= YP_INTERDOMAIN;
213 
214 	key.data = secure_string;
215 	key.size = secure_sz;
216 
217 	if (!(dbp->get)(dbp, &key, &data, 0))
218 		flags |= YP_SECURE;
219 
220 	return(flags);
221 }
222 
223 int yp_testflag(map, domain, flag)
224 	char *map;
225 	char *domain;
226 	int flag;
227 {
228 	char buf[MAXPATHLEN + 2];
229 	register struct circleq_entry *qptr;
230 
231 	if (map == NULL || domain == NULL)
232 		return(0);
233 
234 	strcpy(buf, domain);
235 	strcat(buf, "/");
236 	strcat(buf, map);
237 
238 	for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
239 						qptr = qptr->links.cqe_next) {
240 		if (!strcmp(qptr->dbptr->name, buf)) {
241 			if (qptr->dbptr->flags & flag)
242 				return(1);
243 			else
244 				return(0);
245 		}
246 	}
247 
248 	if (yp_open_db_cache(domain, map, NULL, 0) == NULL)
249 		return(0);
250 
251 	if (qhead.cqh_first->dbptr->flags & flag)
252 		return(1);
253 
254 	return(0);
255 }
256 
257 /*
258  * Add a DB handle and database name to the cache. We only maintain
259  * fixed number of entries in the cache, so if we're asked to store
260  * a new entry when all our slots are already filled, we have to kick
261  * out the entry in the last slot to make room.
262  */
263 static int yp_cache_db(dbp, name, size)
264 	DB *dbp;
265 	char *name;
266 	int size;
267 {
268 	register struct circleq_entry *qptr;
269 
270 	if (numdbs == MAXDBS) {
271 		if (ypdb_debug)
272 			yp_error("queue overflow -- releasing last slot");
273 		yp_flush();
274 	}
275 
276 	/*
277 	 * Allocate a new queue entry.
278 	 */
279 
280 	if ((qptr = yp_malloc_qent()) == NULL) {
281 		yp_error("failed to allocate a new cache entry");
282 		return(1);
283 	}
284 
285 	qptr->dbptr->dbp = dbp;
286 	qptr->dbptr->name = strdup(name);
287 	qptr->dbptr->size = size;
288 	qptr->dbptr->key = NULL;
289 
290 	qptr->dbptr->flags = yp_setflags(dbp);
291 
292 	CIRCLEQ_INSERT_HEAD(&qhead, qptr, links);
293 	numdbs++;
294 
295 	return(0);
296 }
297 
298 /*
299  * Search the list for a database matching 'name.' If we find it,
300  * move it to the head of the list and return its DB handle. If
301  * not, just fail: yp_open_db_cache() will subsequently try to open
302  * the database itself and call yp_cache_db() to add it to the
303  * list.
304  *
305  * The search works like this:
306  *
307  * - The caller specifies the name of a database to locate. We try to
308  *   find an entry in our queue with a matching name.
309  *
310  * - If the caller doesn't specify a key or size, we assume that the
311  *   first entry that we encounter with a matching name is returned.
312  *   This will result in matches regardless of the key/size values
313  *   stored in the queue entry.
314  *
315  * - If the caller also specifies a key and length, we check to see
316  *   if the key and length saved in the queue entry also matches.
317  *   This lets us return a DB handle that's already positioned at the
318  *   correct location within a database.
319  *
320  * - Once we have a match, it gets migrated to the top of the queue
321  *   so that it will be easier to find if another request for
322  *   the same database comes in later.
323  */
324 static DB *yp_find_db(name, key, size)
325 	char *name;
326 	char *key;
327 	int size;
328 {
329 	register struct circleq_entry *qptr;
330 
331 	for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
332 						qptr = qptr->links.cqe_next) {
333 		if (!strcmp(qptr->dbptr->name, name)) {
334 			if (size) {
335 				if (size != qptr->dbptr->size ||
336 				   strncmp(qptr->dbptr->key, key, size))
337 					continue;
338 			} else {
339 				if (qptr->dbptr->size)
340 					continue;
341 			}
342 			if (qptr != qhead.cqh_first) {
343 				CIRCLEQ_REMOVE(&qhead, qptr, links);
344 				CIRCLEQ_INSERT_HEAD(&qhead, qptr, links);
345 			}
346 			return(qptr->dbptr->dbp);
347 		}
348 	}
349 
350 	return(NULL);
351 }
352 
353 /*
354  * Open a DB database and cache the handle for later use. We first
355  * check the cache to see if the required database is already open.
356  * If so, we fetch the handle from the cache. If not, we try to open
357  * the database and save the handle in the cache for later use.
358  */
359 DB *yp_open_db_cache(domain, map, key, size)
360 	const char *domain;
361 	const char *map;
362 	const char *key;
363 	const int size;
364 {
365 	DB *dbp = NULL;
366 	char buf[MAXPATHLEN + 2];
367 /*
368 	snprintf(buf, sizeof(buf), "%s/%s", domain, map);
369 */
370 	yp_errno = YP_TRUE;
371 
372 	strcpy(buf, domain);
373 	strcat(buf, "/");
374 	strcat(buf, map);
375 
376 	if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) {
377 		return(dbp);
378 	} else {
379 		if ((dbp = yp_open_db(domain, map)) != NULL) {
380 			if (yp_cache_db(dbp, (char *)&buf, size)) {
381 				(void)(dbp->close)(dbp);
382 				yp_errno = YP_YPERR;
383 				return(NULL);
384 			}
385 		}
386 	}
387 
388 	return (dbp);
389 }
390 #endif
391 
392 /*
393  * Open a DB database.
394  */
395 DB *yp_open_db(domain, map)
396 	const char *domain;
397 	const char *map;
398 {
399 	DB *dbp = NULL;
400 	char buf[MAXPATHLEN + 2];
401 
402 	yp_errno = YP_TRUE;
403 
404 	if (map[0] == '.' || strchr(map, '/')) {
405 		yp_errno = YP_BADARGS;
406 		return (NULL);
407 	}
408 
409 #ifdef DB_CACHE
410 	if (yp_validdomain(domain)) {
411 		yp_errno = YP_NODOM;
412 		return(NULL);
413 	}
414 #endif
415 	snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
416 
417 #ifdef DB_CACHE
418 again:
419 #endif
420 	dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
421 
422 	if (dbp == NULL) {
423 		switch(errno) {
424 #ifdef DB_CACHE
425 		case ENFILE:
426 			/*
427 			 * We ran out of file descriptors. Nuke an
428 			 * open one and try again.
429 			 */
430 			yp_error("ran out of file descriptors");
431 			yp_flush();
432 			goto again;
433 			break;
434 #endif
435 		case ENOENT:
436 			yp_errno = YP_NOMAP;
437 			break;
438 		case EFTYPE:
439 			yp_errno = YP_BADDB;
440 			break;
441 		default:
442 			yp_errno = YP_YPERR;
443 			break;
444 		}
445 	}
446 
447 	return (dbp);
448 }
449 
450 /*
451  * Database access routines.
452  *
453  * - yp_get_record(): retrieve an arbitrary key/data pair given one key
454  *                 to match against.
455  *
456  * - yp_first_record(): retrieve first key/data base in a database.
457  *
458  * - yp_next_record(): retrieve key/data pair that sequentially follows
459  *                   the supplied key value in the database.
460  */
461 
462 #ifdef DB_CACHE
463 int yp_get_record(dbp,key,data,allow)
464 	DB *dbp;
465 #else
466 int yp_get_record(domain,map,key,data,allow)
467 	const char *domain;
468 	const char *map;
469 #endif
470 	const DBT *key;
471 	DBT *data;
472 	int allow;
473 {
474 #ifndef DB_CACHE
475 	DB *dbp;
476 #endif
477 	int rval = 0;
478 #ifndef DB_CACHE
479 	static unsigned char buf[YPMAXRECORD];
480 #endif
481 
482 	if (ypdb_debug)
483 		yp_error("Looking up key [%.*s]",
484 			  key->size, key->data);
485 
486 	/*
487 	 * Avoid passing back magic "YP_*" entries unless
488 	 * the caller specifically requested them by setting
489 	 * the 'allow' flag.
490 	 */
491 	if (!allow && !strncmp(key->data, "YP_", 3))
492 		return(YP_NOKEY);
493 
494 #ifndef DB_CACHE
495 	if ((dbp = yp_open_db(domain, map)) == NULL) {
496 		return(yp_errno);
497 	}
498 #endif
499 
500 	if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
501 #ifdef DB_CACHE
502 		qhead.cqh_first->dbptr->size = 0;
503 #else
504 		(void)(dbp->close)(dbp);
505 #endif
506 		if (rval == 1)
507 			return(YP_NOKEY);
508 		else
509 			return(YP_BADDB);
510 	}
511 
512 	if (ypdb_debug)
513 		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
514 			 key->size, key->data, data->size, data->data);
515 
516 #ifdef DB_CACHE
517 	if (qhead.cqh_first->dbptr->size) {
518 		qhead.cqh_first->dbptr->key = key->data;
519 		qhead.cqh_first->dbptr->size = key->size;
520 	}
521 #else
522 	bcopy((char *)data->data, (char *)&buf, data->size);
523 	data->data = (void *)&buf;
524 	(void)(dbp->close)(dbp);
525 #endif
526 
527 	return(YP_TRUE);
528 }
529 
530 int yp_first_record(dbp,key,data,allow)
531 	const DB *dbp;
532 	DBT *key;
533 	DBT *data;
534 	int allow;
535 {
536 	int rval;
537 #ifndef DB_CACHE
538 	static unsigned char buf[YPMAXRECORD];
539 #endif
540 
541 	if (ypdb_debug)
542 		yp_error("Retrieving first key in map.");
543 
544 	if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
545 #ifdef DB_CACHE
546 		qhead.cqh_first->dbptr->size = 0;
547 #endif
548 		if (rval == 1)
549 			return(YP_NOKEY);
550 		else
551 			return(YP_BADDB);
552 	}
553 
554 	/* Avoid passing back magic "YP_*" records. */
555 	while (!strncmp(key->data, "YP_", 3) && !allow) {
556 		if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
557 #ifdef DB_CACHE
558 			qhead.cqh_first->dbptr->size = 0;
559 #endif
560 			if (rval == 1)
561 				return(YP_NOKEY);
562 			else
563 				return(YP_BADDB);
564 		}
565 	}
566 
567 	if (ypdb_debug)
568 		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
569 			 key->size, key->data, data->size, data->data);
570 
571 #ifdef DB_CACHE
572 	if (qhead.cqh_first->dbptr->size) {
573 		qhead.cqh_first->dbptr->key = key->data;
574 		qhead.cqh_first->dbptr->size = key->size;
575 	}
576 #else
577 	bcopy((char *)data->data, (char *)&buf, data->size);
578 	data->data = (void *)&buf;
579 #endif
580 
581 	return(YP_TRUE);
582 }
583 
584 int yp_next_record(dbp,key,data,all,allow)
585 	const DB *dbp;
586 	DBT *key;
587 	DBT *data;
588 	int all;
589 	int allow;
590 {
591 	static DBT lkey = { NULL, 0 };
592 	static DBT ldata = { NULL, 0 };
593 	int rval;
594 #ifndef DB_CACHE
595 	static unsigned char keybuf[YPMAXRECORD];
596 	static unsigned char datbuf[YPMAXRECORD];
597 #endif
598 
599 	if (key == NULL || !key->size || key->data == NULL) {
600 		rval = yp_first_record(dbp,key,data,allow);
601 		if (rval == YP_NOKEY)
602 			return(YP_NOMORE);
603 		else {
604 #ifdef DB_CACHE
605 			qhead.cqh_first->dbptr->key = key->data;
606 			qhead.cqh_first->dbptr->size = key->size;
607 #endif
608 			return(rval);
609 		}
610 	}
611 
612 	if (ypdb_debug)
613 		yp_error("Retreiving next key, previous was: [%.*s]",
614 			  key->size, key->data);
615 
616 	if (!all) {
617 #ifdef DB_CACHE
618 		if (qhead.cqh_first->dbptr->key == NULL) {
619 #endif
620 			(dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
621 			while(strncmp((char *)key->data,lkey.data,
622 				(int)key->size) || key->size != lkey.size)
623 				if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
624 #ifdef DB_CACHE
625 					qhead.cqh_first->dbptr->size = 0;
626 #endif
627 					return(YP_NOKEY);
628 				}
629 
630 #ifdef DB_CACHE
631 		}
632 #endif
633 	}
634 
635 	if ((dbp->seq)(dbp,key,data,R_NEXT)) {
636 #ifdef DB_CACHE
637 		qhead.cqh_first->dbptr->size = 0;
638 #endif
639 		return(YP_NOMORE);
640 	}
641 
642 	/* Avoid passing back magic "YP_*" records. */
643 	while (!strncmp(key->data, "YP_", 3) && !allow)
644 		if ((dbp->seq)(dbp,key,data,R_NEXT)) {
645 #ifdef DB_CACHE
646 		qhead.cqh_first->dbptr->size = 0;
647 #endif
648 			return(YP_NOMORE);
649 		}
650 
651 	if (ypdb_debug)
652 		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
653 			 key->size, key->data, data->size, data->data);
654 
655 #ifdef DB_CACHE
656 	if (qhead.cqh_first->dbptr->size) {
657 		qhead.cqh_first->dbptr->key = key->data;
658 		qhead.cqh_first->dbptr->size = key->size;
659 	}
660 #else
661 	bcopy((char *)key->data, (char *)&keybuf, key->size);
662 	lkey.data = (void *)&keybuf;
663 	lkey.size = key->size;
664 	bcopy((char *)data->data, (char *)&datbuf, data->size);
665 	data->data = (void *)&datbuf;
666 #endif
667 
668 	return(YP_TRUE);
669 }
670 
671 #ifdef DB_CACHE
672 /*
673  * Database glue functions.
674  */
675 
676 static DB *yp_currmap_db = NULL;
677 static int yp_allow_db = 0;
678 
679 ypstat yp_select_map(map, domain, key, allow)
680 	char *map;
681 	char *domain;
682 	keydat *key;
683 	int allow;
684 {
685 	yp_currmap_db = yp_open_db_cache(domain, map, key->keydat_val,
686 							key->keydat_len);
687 
688 	yp_allow_db = allow;
689 	return(yp_errno);
690 }
691 
692 ypstat yp_getbykey(key, val)
693 	keydat *key;
694 	valdat *val;
695 {
696 	DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
697 	ypstat rval;
698 
699 	db_key.data = key->keydat_val;
700 	db_key.size = key->keydat_len;
701 
702 	rval = yp_get_record(yp_currmap_db,
703 				&db_key, &db_val, yp_allow_db);
704 
705 	if (rval == YP_TRUE) {
706 		val->valdat_val = db_val.data;
707 		val->valdat_len = db_val.size;
708 	}
709 
710 	return(rval);
711 }
712 
713 ypstat yp_firstbykey(key, val)
714 	keydat *key;
715 	valdat *val;
716 {
717 	DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
718 	ypstat rval;
719 
720 	rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
721 
722 	if (rval == YP_TRUE) {
723 		key->keydat_val = db_key.data;
724 		key->keydat_len = db_key.size;
725 		val->valdat_val = db_val.data;
726 		val->valdat_len = db_val.size;
727 	}
728 
729 	return(rval);
730 }
731 
732 ypstat yp_nextbykey(key, val)
733 	keydat *key;
734 	valdat *val;
735 {
736 	DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
737 	ypstat rval;
738 
739 	db_key.data = key->keydat_val;
740 	db_key.size = key->keydat_len;
741 
742 	rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db);
743 
744 	if (rval == YP_TRUE) {
745 		key->keydat_val = db_key.data;
746 		key->keydat_len = db_key.size;
747 		val->valdat_val = db_val.data;
748 		val->valdat_len = db_val.size;
749 	}
750 
751 	return(rval);
752 }
753 #endif
754