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 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 * 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 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 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 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 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