1/* $OpenBSD: ypserv_db.c,v 1.32 2021/10/09 18:43:51 deraadt Exp $ */ 2 3/* 4 * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se> 5 * Copyright (c) 1996 Charles D. Cranor 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30/* 31 * major revision/cleanup of Mats' version 32 * done by Chuck Cranor <chuck@ccrc.wustl.edu> 33 * Jan 1996. 34 */ 35 36#include <rpc/rpc.h> 37#include <rpcsvc/yp.h> 38#include <rpcsvc/ypclnt.h> 39#include <sys/stat.h> 40#include <fcntl.h> 41#include <string.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <netdb.h> 45#include <resolv.h> 46#include <sys/types.h> 47#include <sys/socket.h> 48#include <sys/queue.h> 49#include <netinet/in.h> 50#include <arpa/inet.h> 51#include <syslog.h> 52#include <errno.h> 53#include "yplog.h" 54#include "ypdb.h" 55#include "ypdef.h" 56#include "ypserv.h" 57 58LIST_HEAD(domainlist, opt_domain); /* LIST of domains */ 59LIST_HEAD(maplist, opt_map); /* LIST of maps (in a domain) */ 60TAILQ_HEAD(mapq, opt_map); /* TAILQ of maps (LRU) */ 61 62struct opt_map { 63 mapname map; /* map name (malloc'd) */ 64 DBM *db; /* database */ 65 struct opt_domain *dom; /* back ptr to our domain */ 66 int host_lookup; /* host lookup */ 67 int secure; /* secure map? */ 68 TAILQ_ENTRY(opt_map) mapsq; /* map queue pointers */ 69 LIST_ENTRY(opt_map) mapsl; /* map list pointers */ 70}; 71 72struct opt_domain { 73 domainname domain; /* domain name (malloc'd) */ 74 struct maplist dmaps; /* the domain's active maps */ 75 LIST_ENTRY(opt_domain) domsl; /* global linked list of domains */ 76}; 77 78struct domainlist doms; /* global list of domains */ 79struct mapq maps; /* global queue of maps (LRU) */ 80 81extern int usedns; 82 83/* 84 * ypdb_init: init the queues and lists 85 */ 86void 87ypdb_init(void) 88{ 89 LIST_INIT(&doms); 90 TAILQ_INIT(&maps); 91} 92 93/* 94 * yp_private: 95 * Check if key is a YP private key. Return TRUE if it is and 96 * ypprivate is FALSE. 97 */ 98static int 99yp_private(datum key, int ypprivate) 100{ 101 if (ypprivate) 102 return (FALSE); 103 104 if (key.dsize == 0 || key.dptr == NULL) 105 return (FALSE); 106 107 if (key.dsize == YP_LAST_LEN && 108 strncmp(key.dptr, YP_LAST_KEY, YP_LAST_LEN) == 0) 109 return(TRUE); 110 if (key.dsize == YP_INPUT_LEN && 111 strncmp(key.dptr, YP_INPUT_KEY, YP_INPUT_LEN) == 0) 112 return(TRUE); 113 if (key.dsize == YP_OUTPUT_LEN && 114 strncmp(key.dptr, YP_OUTPUT_KEY, YP_OUTPUT_LEN) == 0) 115 return(TRUE); 116 if (key.dsize == YP_MASTER_LEN && 117 strncmp(key.dptr, YP_MASTER_KEY, YP_MASTER_LEN) == 0) 118 return(TRUE); 119 if (key.dsize == YP_DOMAIN_LEN && 120 strncmp(key.dptr, YP_DOMAIN_KEY, YP_DOMAIN_LEN) == 0) 121 return(TRUE); 122 if (key.dsize == YP_INTERDOMAIN_LEN && 123 strncmp(key.dptr, YP_INTERDOMAIN_KEY, YP_INTERDOMAIN_LEN) == 0) 124 return(TRUE); 125 if (key.dsize == YP_SECURE_LEN && 126 strncmp(key.dptr, YP_SECURE_KEY, YP_SECURE_LEN) == 0) 127 return(TRUE); 128 return(FALSE); 129} 130 131/* 132 * Close least recent used map. This routine is called when we have 133 * no more file descripotors free, or we want to close all maps. 134 */ 135static void 136ypdb_close_last(void) 137{ 138 struct opt_map *last; 139 140 if (TAILQ_EMPTY(&maps)) { 141 yplog(" ypdb_close_last: LRU list is empty!"); 142 return; 143 } 144 145 last = TAILQ_LAST(&maps, mapq); 146 147 TAILQ_REMOVE(&maps, last, mapsq); /* remove from LRU circleq */ 148 LIST_REMOVE(last, mapsl); /* remove from domain list */ 149 150#ifdef DEBUG 151 yplog(" ypdb_close_last: closing map %s in domain %s [db=0x%x]", 152 last->map, last->dom->domain, last->db); 153#endif 154 155 ypdb_close(last->db); /* close DB */ 156 free(last->map); /* free map name */ 157 free(last); /* free map */ 158} 159 160/* 161 * Close all open maps. 162 */ 163void 164ypdb_close_all(void) 165{ 166 167#ifdef DEBUG 168 yplog(" ypdb_close_all(): start"); 169#endif 170 while (!TAILQ_EMPTY(&maps)) 171 ypdb_close_last(); 172#ifdef DEBUG 173 yplog(" ypdb_close_all(): done"); 174#endif 175} 176 177/* 178 * Close Database if Open/Close Optimization isn't turned on. 179 */ 180static void 181ypdb_close_db(DBM *db) 182{ 183#ifdef DEBUG 184 yplog(" ypdb_close_db(0x%x)", db); 185#endif 186#ifndef OPTDB 187 ypdb_close_all(); 188#endif 189} 190 191/* 192 * ypdb_open_db 193 */ 194DBM * 195ypdb_open_db(domainname domain, mapname map, ypstat *status, 196 struct opt_map **map_info) 197{ 198 char map_path[PATH_MAX]; 199 static char *domain_key = YP_INTERDOMAIN_KEY; 200 static char *secure_key = YP_SECURE_KEY; 201 DBM *db; 202 struct opt_domain *d = NULL; 203 struct opt_map *m = NULL; 204 datum k, v; 205#ifdef OPTDB 206 int i; 207#endif 208 209 /* 210 * check for preloaded domain, map 211 */ 212 LIST_FOREACH(d, &doms, domsl) { 213 if (strcmp(domain, d->domain) == 0) 214 break; 215 } 216 217 if (d) { 218 LIST_FOREACH(m, &d->dmaps, mapsl) 219 if (strcmp(map, m->map) == 0) 220 break; 221 } 222 223 /* 224 * map found open? 225 */ 226 if (m) { 227#ifdef DEBUG 228 yplog(" ypdb_open_db: cached open: domain=%s, map=%s, db=0x%x", 229 domain, map, m->db); 230#endif 231 TAILQ_REMOVE(&maps, m, mapsq); /* adjust LRU queue */ 232 TAILQ_INSERT_HEAD(&maps, m, mapsq); 233 *status = YP_TRUE; 234 if (map_info) 235 *map_info = m; 236 return(m->db); 237 } 238 239 /* Check for illegal charcaters */ 240 241 if (strchr(domain, '/')) { 242 *status = YP_NODOM; 243 return (NULL); 244 } 245 if (strchr(map, '/')) { 246 *status = YP_NOMAP; 247 return (NULL); 248 } 249 250 /* 251 * open map 252 */ 253#ifdef OPTDB 254 i = 0; 255 while (i == 0) { 256#endif 257 snprintf(map_path, sizeof(map_path), "%s/%s/%s", YP_DB_PATH, 258 domain, map); 259 db = ypdb_open(map_path, O_RDONLY, 0444); 260#ifdef OPTDB 261 if (db == NULL) { 262#ifdef DEBUG 263 yplog(" ypdb_open_db: errno %d (%s)", 264 errno, sys_errlist[errno]); 265#endif 266 if (errno == ENFILE || errno == EMFILE) { 267 ypdb_close_last(); 268 } else { 269 i = errno; 270 } 271 } else { 272 i = 4711; 273 } 274 } 275#endif 276 *status = YP_NOMAP; /* see note below */ 277 if (db == NULL) { 278 if (errno == ENOENT) { 279#ifdef DEBUG 280 yplog(" ypdb_open_db: no map %s (domain=%s)", 281 map, domain); 282#endif 283 return(NULL); 284 } 285#ifdef DEBUG 286 yplog(" ypdb_open_db: ypdb_open FAILED: map %s (domain=%s)", 287 map, domain); 288#endif 289 return(NULL); 290 } 291 292 /* 293 * note: status now YP_NOMAP 294 */ 295 296 if (d == NULL) { /* allocate new domain? */ 297 d = malloc(sizeof(*d)); 298 if (d) 299 d->domain = strdup(domain); 300 if (d == NULL || d->domain == NULL) { 301 yplog(" ypdb_open_db: MALLOC failed"); 302 ypdb_close(db); 303 free(d); 304 return(NULL); 305 } 306 LIST_INIT(&d->dmaps); 307 LIST_INSERT_HEAD(&doms, d, domsl); 308#ifdef DEBUG 309 yplog(" ypdb_open_db: NEW DOMAIN %s", domain); 310#endif 311 } 312 313 /* 314 * m must be NULL since we couldn't find a map. allocate new one 315 */ 316 317 m = malloc(sizeof(*m)); 318 if (m) 319 m->map = strdup(map); 320 if (m == NULL || m->map == NULL) { 321 free(m); 322 yplog(" ypdb_open_db: MALLOC failed"); 323 ypdb_close(db); 324 return(NULL); 325 } 326 m->db = db; 327 m->dom = d; 328 m->host_lookup = FALSE; 329 TAILQ_INSERT_HEAD(&maps, m, mapsq); 330 LIST_INSERT_HEAD(&d->dmaps, m, mapsl); 331 if (strcmp(map, YP_HOSTNAME) == 0 || strcmp(map, YP_HOSTADDR) == 0) { 332 if (!usedns) { 333 k.dptr = domain_key; 334 k.dsize = YP_INTERDOMAIN_LEN; 335 v = ypdb_fetch(db, k); 336 if (v.dptr) 337 m->host_lookup = TRUE; 338 } else 339 m->host_lookup = TRUE; 340 } 341 m->secure = FALSE; 342 k.dptr = secure_key; 343 k.dsize = YP_SECURE_LEN; 344 v = ypdb_fetch(db, k); 345 if (v.dptr) 346 m->secure = TRUE; 347 *status = YP_TRUE; 348 if (map_info) 349 *map_info = m; 350#ifdef DEBUG 351 yplog(" ypdb_open_db: NEW MAP domain=%s, map=%s, hl=%d, s=%d, db=0x%x", 352 domain, map, m->host_lookup, m->secure, m->db); 353#endif 354 return(m->db); 355} 356 357/* 358 * lookup host 359 */ 360static ypstat 361lookup_host(int nametable, int host_lookup, DBM *db, char *keystr, 362 ypresp_val *result) 363{ 364 struct hostent *host; 365 struct in_addr *addr_name; 366 struct in_addr addr_addr; 367 static char val[BUFSIZ+1]; /* match libc */ 368 static char hostname[HOST_NAME_MAX+1]; 369 char tmpbuf[HOST_NAME_MAX+1 + 20], *v, *ptr; 370 size_t len; 371 int l; 372 373 if (!host_lookup) 374 return(YP_NOKEY); 375 376 if ((_res.options & RES_INIT) == 0) 377 res_init(); 378 bcopy("b", _res.lookups, sizeof("b")); 379 380 if (nametable) { 381 host = gethostbyname(keystr); 382 if (host == NULL || host->h_addrtype != AF_INET) 383 return(YP_NOKEY); 384 addr_name = (struct in_addr *) *host->h_addr_list; 385 v = val; 386 for (; host->h_addr_list[0] != NULL; host->h_addr_list++) { 387 addr_name = (struct in_addr *)host->h_addr_list[0]; 388 snprintf(tmpbuf, sizeof(tmpbuf), "%s %s\n", 389 inet_ntoa(*addr_name), host->h_name); 390 len = strlcat(val, tmpbuf, sizeof(val)); 391 if (len >= sizeof(val)) 392 break; 393 v = val + len; 394 } 395 result->val.valdat_val = val; 396 result->val.valdat_len = v - val; 397 return(YP_TRUE); 398 } 399 400 inet_aton(keystr, &addr_addr); 401 host = gethostbyaddr((char *)&addr_addr, sizeof(addr_addr), AF_INET); 402 if (host == NULL) return(YP_NOKEY); 403 404 strncpy((char *)hostname, host->h_name, sizeof(hostname) - 1); 405 hostname[sizeof(hostname) - 1] = '\0'; 406 host = gethostbyname((char *)hostname); 407 if (host == NULL) 408 return(YP_NOKEY); 409 410 l = 0; 411 for (; host->h_addr_list[0] != NULL; host->h_addr_list++) 412 if (!bcmp(host->h_addr_list[0], &addr_addr, sizeof(addr_addr))) 413 l++; 414 if (l == 0) { 415 yplog("lookup_host: address %s not listed for host %s\n", 416 inet_ntoa(addr_addr), hostname); 417 syslog(LOG_NOTICE, 418 "ypserv: address %s not listed for host %s\n", 419 inet_ntoa(addr_addr), hostname); 420 return(YP_NOKEY); 421 } 422 423 len = snprintf(val, sizeof(val), "%s %s", keystr, host->h_name); 424 if (len < 0 || len >= sizeof(val)) 425 return(YP_YPERR); 426 v = val + len; 427 while ((ptr = *(host->h_aliases)) != NULL) { 428 strlcat(val, " ", sizeof(val)); 429 len = strlcat(val, ptr, sizeof(val)); 430 if (len >= sizeof(val)) 431 break; 432 v = val + len; 433 host->h_aliases++; 434 } 435 result->val.valdat_val = val; 436 result->val.valdat_len = v - val; 437 438 return(YP_TRUE); 439} 440 441ypresp_val 442ypdb_get_record(domainname domain, mapname map, keydat key, int ypprivate) 443{ 444 static ypresp_val res; 445 static char keystr[YPMAXRECORD+1]; 446 DBM *db; 447 datum k, v; 448 int host_lookup = 0, hn; 449 struct opt_map *map_info = NULL; 450 451 bzero(&res, sizeof(res)); 452 453 db = ypdb_open_db(domain, map, &res.stat, &map_info); 454 if (!db || res.stat < 0) 455 return(res); 456 if (map_info) 457 host_lookup = map_info->host_lookup; 458 459 k.dptr = key.keydat_val; 460 k.dsize = key.keydat_len; 461 462 if (yp_private(k, ypprivate)) { 463 res.stat = YP_NOKEY; 464 goto done; 465 } 466 467 v = ypdb_fetch(db, k); 468 469 if (v.dptr == NULL) { 470 res.stat = YP_NOKEY; 471 if ((hn = strcmp(map, YP_HOSTNAME)) != 0 && 472 strcmp(map, YP_HOSTADDR) != 0) 473 goto done; 474 /* note: lookup_host needs null terminated string */ 475 strncpy(keystr, key.keydat_val, key.keydat_len); 476 keystr[key.keydat_len] = '\0'; 477 res.stat = lookup_host((hn == 0) ? TRUE : FALSE, 478 host_lookup, db, keystr, &res); 479 } else { 480 res.val.valdat_val = v.dptr; 481 res.val.valdat_len = v.dsize; 482 } 483 484done: 485 ypdb_close_db(db); 486 return(res); 487} 488 489ypresp_key_val 490ypdb_get_first(domainname domain, mapname map, int ypprivate) 491{ 492 static ypresp_key_val res; 493 DBM *db; 494 datum k, v; 495 496 bzero(&res, sizeof(res)); 497 498 db = ypdb_open_db(domain, map, &res.stat, NULL); 499 500 if (res.stat >= 0) { 501 k = ypdb_firstkey(db); 502 503 while (yp_private(k, ypprivate)) 504 k = ypdb_nextkey(db); 505 506 if (k.dptr == NULL) { 507 res.stat = YP_NOKEY; 508 } else { 509 res.key.keydat_val = k.dptr; 510 res.key.keydat_len = k.dsize; 511 v = ypdb_fetch(db, k); 512 if (v.dptr == NULL) { 513 res.stat = YP_NOKEY; 514 } else { 515 res.val.valdat_val = v.dptr; 516 res.val.valdat_len = v.dsize; 517 } 518 } 519 } 520 ypdb_close_db(db); 521 return (res); 522} 523 524ypresp_key_val 525ypdb_get_next(domainname domain, mapname map, keydat key, int ypprivate) 526{ 527 static ypresp_key_val res; 528 DBM *db; 529 datum k, v, n; 530 531 bzero(&res, sizeof(res)); 532 db = ypdb_open_db(domain, map, &res.stat, NULL); 533 534 if (res.stat >= 0) { 535 n.dptr = key.keydat_val; 536 n.dsize = key.keydat_len; 537 v.dptr = NULL; 538 v.dsize = 0; 539 k.dptr = NULL; 540 k.dsize = 0; 541 542 n = ypdb_setkey(db, n); 543 544 if (n.dptr != NULL) 545 k = ypdb_nextkey(db); 546 else 547 k.dptr = NULL; 548 549 if (k.dptr != NULL) { 550 while (yp_private(k, ypprivate)) 551 k = ypdb_nextkey(db); 552 } 553 554 if (k.dptr == NULL) { 555 res.stat = YP_NOMORE; 556 } else { 557 res.key.keydat_val = k.dptr; 558 res.key.keydat_len = k.dsize; 559 v = ypdb_fetch(db, k); 560 if (v.dptr == NULL) { 561 res.stat = YP_NOMORE; 562 } else { 563 res.val.valdat_val = v.dptr; 564 res.val.valdat_len = v.dsize; 565 } 566 } 567 } 568 ypdb_close_db(db); 569 return (res); 570} 571 572ypresp_order 573ypdb_get_order(domainname domain, mapname map) 574{ 575 static ypresp_order res; 576 static char *order_key = YP_LAST_KEY; 577 char order[MAX_LAST_LEN+1]; 578 DBM *db; 579 datum k, v; 580 581 bzero(&res, sizeof(res)); 582 db = ypdb_open_db(domain, map, &res.stat, NULL); 583 584 if (res.stat >= 0) { 585 k.dptr = order_key; 586 k.dsize = YP_LAST_LEN; 587 588 v = ypdb_fetch(db, k); 589 if (v.dptr == NULL) { 590 res.stat = YP_NOKEY; 591 } else { 592 strncpy(order, v.dptr, v.dsize); 593 order[v.dsize] = '\0'; 594 res.ordernum = (u_int32_t)atol(order); 595 } 596 } 597 ypdb_close_db(db); 598 return (res); 599} 600 601ypresp_master 602ypdb_get_master(domainname domain, mapname map) 603{ 604 static ypresp_master res; 605 static char *master_key = YP_MASTER_KEY; 606 static char master[MAX_MASTER_LEN+1]; 607 DBM *db; 608 datum k, v; 609 610 bzero(&res, sizeof(res)); 611 db = ypdb_open_db(domain, map, &res.stat, NULL); 612 613 if (res.stat >= 0) { 614 k.dptr = master_key; 615 k.dsize = YP_MASTER_LEN; 616 617 v = ypdb_fetch(db, k); 618 if (v.dptr == NULL) { 619 res.stat = YP_NOKEY; 620 } else { 621 strncpy(master, v.dptr, v.dsize); 622 master[v.dsize] = '\0'; 623 res.peer = (peername) &master; 624 } 625 } 626 ypdb_close_db(db); 627 return (res); 628} 629 630bool_t 631ypdb_xdr_get_all(XDR *xdrs, ypreq_nokey *req) 632{ 633 static ypresp_all resp; 634 DBM *db; 635 datum k, v; 636 637 bzero(&resp, sizeof(resp)); 638 639 /* 640 * open db, and advance past any private keys we may see 641 */ 642 db = ypdb_open_db(req->domain, req->map, 643 &resp.ypresp_all_u.val.stat, NULL); 644 if (!db || resp.ypresp_all_u.val.stat < 0) 645 return(FALSE); 646 k = ypdb_firstkey(db); 647 while (yp_private(k, FALSE)) 648 k = ypdb_nextkey(db); 649 650 while (1) { 651 if (k.dptr == NULL) 652 break; 653 654 v = ypdb_fetch(db, k); 655 if (v.dptr == NULL) 656 break; 657 658 resp.more = TRUE; 659 resp.ypresp_all_u.val.stat = YP_TRUE; 660 resp.ypresp_all_u.val.key.keydat_val = k.dptr; 661 resp.ypresp_all_u.val.key.keydat_len = k.dsize; 662 resp.ypresp_all_u.val.val.valdat_val = v.dptr; 663 resp.ypresp_all_u.val.val.valdat_len = v.dsize; 664 665 if (!xdr_ypresp_all(xdrs, &resp)) { 666#ifdef DEBUG 667 yplog(" ypdb_xdr_get_all: xdr_ypresp_all failed"); 668#endif 669 return(FALSE); 670 } 671 672 /* advance past private keys */ 673 k = ypdb_nextkey(db); 674 while (yp_private(k, FALSE)) 675 k = ypdb_nextkey(db); 676 } 677 678 bzero(&resp, sizeof(resp)); 679 resp.ypresp_all_u.val.stat = YP_NOKEY; 680 resp.more = FALSE; 681 682 if (!xdr_ypresp_all(xdrs, &resp)) { 683#ifdef DEBUG 684 yplog(" ypdb_xdr_get_all: final xdr_ypresp_all failed"); 685#endif 686 return(FALSE); 687 } 688 ypdb_close_db(db); 689 return (TRUE); 690} 691 692int 693ypdb_secure(domainname domain, mapname map) 694{ 695 static ypresp_val res; 696 DBM *db; 697 int secure; 698 struct opt_map *map_info = NULL; 699 700 bzero(&res, sizeof(res)); 701 secure = FALSE; 702 703 db = ypdb_open_db(domain, map, &res.stat, &map_info); 704 if (!db || res.stat < 0) 705 return(secure); /* ? */ 706 if (map_info) 707 secure = map_info->secure; 708 709 ypdb_close_db(db); 710 return(secure); 711} 712