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