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