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