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: stable/10/usr.sbin/ypserv/yp_dblookup.c 114601 2003-05-03 21:06:42Z obrien $");
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
yp_init_dbs(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 *
yp_malloc_qent(void)102 yp_malloc_qent(void)
103 {
104 register struct circleq_entry *q;
105
106 q = (struct circleq_entry *)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 = (struct dbent *)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
yp_free_qent(struct circleq_entry * q)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
yp_flush(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
yp_flush_all(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
yp_setflags(DB * dbp)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
yp_testflag(char * map,char * domain,int flag)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
yp_cache_db(DB * dbp,char * name,int size)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 *
yp_find_db(const char * name,const char * key,int size)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 *
yp_open_db_cache(const char * domain,const char * map,const char * key,const int size)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 *
yp_open_db(const char * domain,const char * map)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
yp_get_record(DB * dbp,const DBT * key,DBT * data,int allow)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
yp_first_record(const DB * dbp,DBT * key,DBT * data,int allow)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
yp_next_record(const DB * dbp,DBT * key,DBT * data,int all,int allow)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
yp_select_map(char * map,char * domain,keydat * key,int allow)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
yp_getbykey(keydat * key,valdat * val)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
yp_firstbykey(keydat * key,valdat * val)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
yp_nextbykey(keydat * key,valdat * val)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