1/* $OpenBSD: mapc.c,v 1.24 2021/10/21 10:55:56 deraadt Exp $ */ 2 3/*- 4 * Copyright (c) 1989 Jan-Simon Pendry 5 * Copyright (c) 1989 Imperial College of Science, Technology & Medicine 6 * Copyright (c) 1989, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * This code is derived from software contributed to Berkeley by 10 * Jan-Simon Pendry at Imperial College, London. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37/* 38 * Mount map cache 39 */ 40 41#include "am.h" 42#include <regex.h> 43 44/* 45 * Hash table size 46 */ 47#define NKVHASH (1 << 2) /* Power of two */ 48 49/* 50 * Wildcard key 51 */ 52static char wildcard[] = "*"; 53 54/* 55 * Map cache types 56 * default, none, incremental, all, regexp 57 * MAPC_RE implies MAPC_ALL and must be numerically 58 * greater. 59 */ 60#define MAPC_DFLT 0x000 61#define MAPC_NONE 0x001 62#define MAPC_INC 0x002 63#define MAPC_ROOT 0x004 64#define MAPC_ALL 0x010 65#define MAPC_RE 0x020 66#define MAPC_ISRE(m) ((m)->alloc == MAPC_RE) 67#define MAPC_CACHE_MASK 0x0ff 68#define MAPC_SYNC 0x100 69 70static struct opt_tab mapc_opt[] = { 71 { "all", MAPC_ALL }, 72 { "default", MAPC_DFLT }, 73 { "inc", MAPC_INC }, 74 { "mapdefault", MAPC_DFLT }, 75 { "none", MAPC_NONE }, 76 { "re", MAPC_RE }, 77 { "regexp", MAPC_RE }, 78 { "sync", MAPC_SYNC }, 79 { 0, 0 } 80}; 81 82/* 83 * Lookup recursion 84 */ 85#define MREC_FULL 2 86#define MREC_PART 1 87#define MREC_NONE 0 88 89/* 90 * Cache map operations 91 */ 92typedef void add_fn(mnt_map *, char *, char *); 93typedef int init_fn(char *, time_t *); 94typedef int search_fn(mnt_map *, char *, char *, char **, time_t *); 95typedef int reload_fn(mnt_map *, char *, add_fn *); 96typedef int mtime_fn(char *, time_t *); 97 98static void mapc_sync(mnt_map *); 99 100/* 101 * Map type 102 */ 103typedef struct map_type map_type; 104struct map_type { 105 char *name; /* Name of this map type */ 106 init_fn *init; /* Initialisation */ 107 reload_fn *reload; /* Reload or fill */ 108 search_fn *search; /* Search for new entry */ 109 mtime_fn *mtime; /* Find modify time */ 110 int def_alloc; /* Default allocation mode */ 111}; 112 113/* 114 * Key-value pair 115 */ 116typedef struct kv kv; 117struct kv { 118 kv *next; 119 char *key; 120 char *val; 121}; 122 123struct mnt_map { 124 qelem hdr; 125 int refc; /* Reference count */ 126 short flags; /* Allocation flags */ 127 short alloc; /* Allocation mode */ 128 time_t modify; /* Modify time of map */ 129 char *map_name; /* Name of this map */ 130 char *wildcard; /* Wildcard value */ 131 reload_fn *reload; /* Function to be used for reloads */ 132 search_fn *search; /* Function to be used for searching */ 133 mtime_fn *mtime; /* Modify time function */ 134 kv *kvhash[NKVHASH]; /* Cached data */ 135}; 136 137/* 138 * Map for root node 139 */ 140static mnt_map *root_map; 141 142/* 143 * List of known maps 144 */ 145extern qelem map_list_head; 146qelem map_list_head = { &map_list_head, &map_list_head }; 147 148/* 149 * Configuration 150 */ 151 152/* ROOT MAP */ 153static int root_init(char *, time_t *); 154 155/* FILE MAPS */ 156extern int file_init(char *, time_t *); 157extern int file_reload(mnt_map *, char *, add_fn *); 158extern int file_search(mnt_map *, char *, char *, char **, time_t *); 159extern int file_mtime(char *, time_t *); 160 161/* Network Information Service (NIS) MAPS */ 162extern int nis_init(char *, time_t *); 163extern int nis_reload(mnt_map *, char *, add_fn *); 164extern int nis_search(mnt_map *, char *, char *, char **, time_t *); 165#define nis_mtime nis_init 166 167/* NDBM MAPS */ 168#ifdef HAS_NDBM_MAPS 169extern int ndbm_init(char *, time_t *); 170extern int ndbm_search(mnt_map *, char *, charo *, char **, time_t *); 171#define ndbm_mtime ndbm_init 172#endif /* HAS_NDBM_MAPS */ 173 174/* PASSWD MAPS */ 175extern int passwd_init(char *, time_t *); 176extern int passwd_search(mnt_map *, char *, char *, char **, time_t *); 177 178/* UNION MAPS */ 179extern int union_init(char *, time_t *); 180extern int union_search(mnt_map *, char *, char *, char **, time_t *); 181extern int union_reload(mnt_map *, char *, add_fn *); 182 183/* ERROR MAP */ 184static int error_init(char *, time_t *); 185static int error_reload(mnt_map *, char *, add_fn *); 186static int error_search(mnt_map *, char *, char *, char **, time_t *); 187static int error_mtime(char *, time_t *); 188 189static map_type maptypes[] = { 190 { "root", root_init, error_reload, error_search, error_mtime, MAPC_ROOT }, 191 192 { "passwd", passwd_init, error_reload, passwd_search, error_mtime, MAPC_INC }, 193 194 { "union", union_init, union_reload, union_search, error_mtime, MAPC_ALL }, 195 196 { "nis", nis_init, nis_reload, nis_search, nis_mtime, MAPC_INC }, 197 198#ifdef HAS_NDBM_MAPS 199 { "ndbm", ndbm_init, error_reload, ndbm_search, ndbm_mtime, MAPC_INC }, 200#endif 201 202 { "file", file_init, file_reload, file_search, file_mtime, MAPC_ALL }, 203 204 { "error", error_init, error_reload, error_search, error_mtime, MAPC_NONE }, 205}; 206 207/* 208 * Hash function 209 */ 210static unsigned int 211kvhash_of(char *key) 212{ 213 unsigned int i, j; 214 215 for (i = 0; (j = *key++); i += j) 216 ; 217 218 return i % NKVHASH; 219} 220 221void 222mapc_showtypes(FILE *fp) 223{ 224 map_type *mt; 225 char *sep = ""; 226 227 for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++) { 228 fprintf(fp, "%s%s", sep, mt->name); 229 sep = ", "; 230 } 231} 232 233/* 234 * Add key and val to the map m. 235 * key and val are assumed to be safe copies 236 */ 237void 238mapc_add_kv(mnt_map *m, char *key, char *val) 239{ 240 kv **h; 241 kv *n; 242 int hash = kvhash_of(key); 243 244#ifdef DEBUG 245 dlog("add_kv: %s -> %s", key, val); 246#endif 247 248 if (MAPC_ISRE(m)) { 249 char keyb[PATH_MAX]; 250 regex_t *re; 251 int err; 252 253 /* 254 * Make sure the string is bound to the start and end 255 */ 256 snprintf(keyb, sizeof(keyb), "^%s$", key); 257 re = malloc(sizeof(*re)); 258 if (re == NULL) { 259 plog(XLOG_USER, "error allocating RE \"%s\"", keyb); 260 return; 261 } 262 err = regcomp(re, keyb, 0); 263 if (err) { 264 char errbuf[100]; 265 266 regerror(err, re, errbuf, sizeof errbuf); 267 free(re); 268 plog(XLOG_USER, "error compiling RE \"%s\": %s", 269 keyb, errbuf); 270 return; 271 } 272 273 free(key); 274 key = (char *)re; 275 } 276 277 h = &m->kvhash[hash]; 278 n = ALLOC(kv); 279 n->key = key; 280 n->val = val; 281 n->next = *h; 282 *h = n; 283} 284 285static void 286mapc_repl_kv(mnt_map *m, char *key, char *val) 287{ 288 kv *k; 289 290 /* 291 * Compute the hash table offset 292 */ 293 k = m->kvhash[kvhash_of(key)]; 294 295 /* 296 * Scan the linked list for the key 297 */ 298 while (k && !FSTREQ(k->key, key)) 299 k = k->next; 300 301 if (k) { 302 free(k->val); 303 k->val = val; 304 } else { 305 mapc_add_kv(m, key, val); 306 } 307 308} 309 310/* 311 * Search a map for a key. 312 * Calls map specific search routine. 313 * While map is out of date, keep re-syncing. 314 */ 315static int search_map(mnt_map *m, char *key, char **valp) 316{ 317 int rc; 318 319 do { 320 rc = (*m->search)(m, m->map_name, key, valp, &m->modify); 321 if (rc < 0) { 322 plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name); 323 mapc_sync(m); 324 } 325 } while (rc < 0); 326 327 return rc; 328} 329 330/* 331 * Do a wildcard lookup in the map and 332 * save the result. 333 */ 334static void 335mapc_find_wildcard(mnt_map *m) 336{ 337 /* 338 * Attempt to find the wildcard entry 339 */ 340 int rc = search_map(m, wildcard, &m->wildcard); 341 342 if (rc != 0) 343 m->wildcard = 0; 344} 345 346/* 347 * Make a duplicate reference to an existing map 348 */ 349#define mapc_dup(m) ((m)->refc++, (m)) 350 351/* 352 * Do a map reload 353 */ 354static int 355mapc_reload_map(mnt_map *m) 356{ 357 int error; 358#ifdef DEBUG 359 dlog("calling map reload on %s", m->map_name); 360#endif 361 error = (*m->reload)(m, m->map_name, mapc_add_kv); 362 if (error) 363 return error; 364 m->wildcard = 0; 365#ifdef DEBUG 366 dlog("calling mapc_search for wildcard"); 367#endif 368 error = mapc_search(m, wildcard, &m->wildcard); 369 if (error) 370 m->wildcard = 0; 371 return 0; 372} 373 374/* 375 * Create a new map 376 */ 377static mnt_map * 378mapc_create(char *map, char *opt) 379{ 380 mnt_map *m = ALLOC(mnt_map); 381 map_type *mt; 382 time_t modify; 383 int alloc = 0; 384 385 (void) cmdoption(opt, mapc_opt, &alloc); 386 387 for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++) 388 if ((*mt->init)(map, &modify) == 0) 389 break; 390 /* assert: mt in maptypes */ 391 392 m->flags = alloc & ~MAPC_CACHE_MASK; 393 alloc &= MAPC_CACHE_MASK; 394 395 if (alloc == MAPC_DFLT) 396 alloc = mt->def_alloc; 397 switch (alloc) { 398 default: 399 plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt); 400 alloc = MAPC_INC; 401 /* fallthrough... */ 402 case MAPC_NONE: 403 case MAPC_INC: 404 case MAPC_ROOT: 405 break; 406 case MAPC_ALL: 407 /* 408 * If there is no support for reload and it was requested 409 * then back off to incremental instead. 410 */ 411 if (mt->reload == error_reload) { 412 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name); 413 alloc = MAPC_INC; 414 } 415 break; 416 case MAPC_RE: 417 if (mt->reload == error_reload) { 418 plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name); 419 mt = &maptypes[sizeof(maptypes)/sizeof(maptypes[0]) - 1]; 420 /* assert: mt->name == "error" */ 421 } 422 break; 423 } 424 425#ifdef DEBUG 426 dlog("Map for %s coming from maptype %s", map, mt->name); 427#endif 428 429 m->alloc = alloc; 430 m->reload = mt->reload; 431 m->modify = modify; 432 m->search = alloc >= MAPC_ALL ? error_search : mt->search; 433 m->mtime = mt->mtime; 434 bzero(m->kvhash, sizeof(m->kvhash)); 435 m->map_name = strdup(map); 436 m->refc = 1; 437 m->wildcard = 0; 438 439 /* 440 * synchronize cache with reality 441 */ 442 mapc_sync(m); 443 444 return m; 445} 446 447/* 448 * Free the cached data in a map 449 */ 450static void 451mapc_clear(mnt_map *m) 452{ 453 int i; 454 455 /* 456 * For each of the hash slots, chain 457 * along free'ing the data. 458 */ 459 for (i = 0; i < NKVHASH; i++) { 460 kv *k = m->kvhash[i]; 461 while (k) { 462 kv *n = k->next; 463 free(k->key); 464 free(k->val); 465 free(k); 466 k = n; 467 } 468 } 469 /* 470 * Zero the hash slots 471 */ 472 bzero(m->kvhash, sizeof(m->kvhash)); 473 /* 474 * Free the wildcard if it exists 475 */ 476 if (m->wildcard) { 477 free(m->wildcard); 478 m->wildcard = 0; 479 } 480} 481 482/* 483 * Find a map, or create one if it does not exist 484 */ 485mnt_map * 486mapc_find(char *map, char *opt) 487{ 488 mnt_map *m; 489 490 /* 491 * Search the list of known maps to see if 492 * it has already been loaded. If it is found 493 * then return a duplicate reference to it. 494 * Otherwise make a new map as required and 495 * add it to the list of maps 496 */ 497 ITER(m, mnt_map, &map_list_head) 498 if (STREQ(m->map_name, map)) 499 return mapc_dup(m); 500 501 m = mapc_create(map, opt); 502 ins_que(&m->hdr, &map_list_head); 503 return m; 504} 505 506/* 507 * Free a map. 508 */ 509void 510mapc_free(void *arg) 511{ 512 mnt_map *m = arg; 513 /* 514 * Decrement the reference count. 515 * If the reference count hits zero 516 * then throw the map away. 517 */ 518 if (m && --m->refc == 0) { 519 mapc_clear(m); 520 free(m->map_name); 521 rem_que(&m->hdr); 522 free(m); 523 } 524} 525 526/* 527 * Search the map for the key. 528 * Put a safe copy in *pval or return 529 * an error code 530 */ 531int 532mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse) 533{ 534 int error = 0; 535 kv *k = 0; 536 537 /* 538 * Firewall 539 */ 540 if (!m) { 541 plog(XLOG_ERROR, "Null map request for %s", key); 542 return ENOENT; 543 } 544 545 if (m->flags & MAPC_SYNC) { 546 /* 547 * Get modify time... 548 */ 549 time_t t; 550 error = (*m->mtime)(m->map_name, &t); 551 if (error || t > m->modify) { 552 m->modify = t; 553 plog(XLOG_INFO, "Map %s is out of date", m->map_name); 554 mapc_sync(m); 555 } 556 } 557 558 if (!MAPC_ISRE(m)) { 559 /* 560 * Compute the hash table offset 561 */ 562 k = m->kvhash[kvhash_of(key)]; 563 564 /* 565 * Scan the linked list for the key 566 */ 567 while (k && !FSTREQ(k->key, key)) k = k->next; 568 569 } 570 else if (recurse == MREC_FULL) { 571 /* 572 * Try for an RE match against the entire map. 573 * Note that this will be done in a "random" 574 * order. 575 */ 576 577 int i; 578 579 for (i = 0; i < NKVHASH; i++) { 580 k = m->kvhash[i]; 581 while (k) { 582 if (regexec((regex_t *)k->key, key, 583 0, NULL, 0) == 0) 584 break; 585 k = k->next; 586 } 587 if (k) 588 break; 589 } 590 } 591 592 /* 593 * If found then take a copy 594 */ 595 if (k) { 596 if (k->val) 597 *pval = strdup(k->val); 598 else 599 error = ENOENT; 600 } else if (m->alloc >= MAPC_ALL) { 601 /* 602 * If the entire map is cached then this 603 * key does not exist. 604 */ 605 error = ENOENT; 606 } else { 607 /* 608 * Otherwise search the map. If we are 609 * in incremental mode then add the key 610 * to the cache. 611 */ 612 error = search_map(m, key, pval); 613 if (!error && m->alloc == MAPC_INC) 614 mapc_add_kv(m, strdup(key), strdup(*pval)); 615 } 616 617 /* 618 * If an error, and a wildcard exists, 619 * and the key is not internal then 620 * return a copy of the wildcard. 621 */ 622 if (error > 0) { 623 if (recurse == MREC_FULL && !MAPC_ISRE(m)) { 624 char wildname[PATH_MAX]; 625 char *subp; 626 if (*key == '/') 627 return error; 628 /* 629 * Keep chopping sub-directories from the RHS 630 * and replacing with "/ *" and repeat the lookup. 631 * For example: 632 * "src/gnu/gcc" -> "src / gnu / *" -> "src / *" 633 */ 634 strlcpy(wildname, key, sizeof wildname); 635 while (error && (subp = strrchr(wildname, '/'))) { 636 strlcpy(subp, "/*", 3); 637#ifdef DEBUG 638 dlog("mapc recurses on %s", wildname); 639#endif 640 error = mapc_meta_search(m, wildname, pval, MREC_PART); 641 if (error) 642 *subp = 0; 643 } 644 if (error > 0 && m->wildcard) { 645 *pval = strdup(m->wildcard); 646 error = 0; 647 } 648 } 649 } 650 651 return error; 652} 653 654int 655mapc_search(mnt_map *m, char *key, char **pval) 656{ 657 return mapc_meta_search(m, key, pval, MREC_FULL); 658} 659 660/* 661 * Get map cache in sync with physical representation 662 */ 663static void 664mapc_sync(mnt_map *m) 665{ 666 if (m->alloc != MAPC_ROOT) { 667 mapc_clear(m); 668 669 if (m->alloc >= MAPC_ALL) 670 if (mapc_reload_map(m)) 671 m->alloc = MAPC_INC; 672 /* 673 * Attempt to find the wildcard entry 674 */ 675 if (m->alloc < MAPC_ALL) 676 mapc_find_wildcard(m); 677 } 678} 679 680/* 681 * Reload all the maps 682 * Called when Amd gets hit by a SIGHUP. 683 */ 684void mapc_reload(void) 685{ 686 mnt_map *m; 687 688 /* 689 * For all the maps, 690 * Throw away the existing information. 691 * Do a reload 692 * Find the wildcard 693 */ 694 ITER(m, mnt_map, &map_list_head) 695 mapc_sync(m); 696} 697 698/* 699 * Root map. 700 * The root map is used to bootstrap amd. 701 * All the require top-level mounts are added 702 * into the root map and then the map is iterated 703 * and a lookup is done on all the mount points. 704 * This causes the top level mounts to be automounted. 705 */ 706 707static int 708root_init(char *map, time_t *tp) 709{ 710 *tp = clocktime(); 711 return strcmp(map, ROOT_MAP) == 0 ? 0 : ENOENT; 712} 713 714/* 715 * Add a new entry to the root map 716 * 717 * dir - directory (key) 718 * opts - mount options 719 * map - map name 720 */ 721void 722root_newmap(char *dir, char *opts, char *map) 723{ 724 char str[PATH_MAX]; 725 726 /* 727 * First make sure we have a root map to talk about... 728 */ 729 if (!root_map) 730 root_map = mapc_find(ROOT_MAP, "mapdefault"); 731 732 /* 733 * Then add the entry... 734 */ 735 dir = strdup(dir); 736 if (map) 737 snprintf(str, sizeof(str), "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s", 738 map, opts ? opts : ""); 739 else 740 strlcpy(str, opts, sizeof str); 741 mapc_repl_kv(root_map, dir, strdup(str)); 742} 743 744int 745mapc_keyiter(mnt_map *m, void (*fn)(char *,void *), void *arg) 746{ 747 int i; 748 int c = 0; 749 750 for (i = 0; i < NKVHASH; i++) { 751 kv *k = m->kvhash[i]; 752 while (k) { 753 (*fn)(k->key, arg); 754 k = k->next; 755 c++; 756 } 757 } 758 759 return c; 760} 761 762/* 763 * Iterate over the root map 764 * and call (*fn)() on the key 765 * of all the nodes. 766 * Finally throw away the root map. 767 */ 768int 769root_keyiter(void (*fn)(char *,void *), void *arg) 770{ 771 if (root_map) { 772 int c = mapc_keyiter(root_map, fn, arg); 773#ifdef notdef 774 mapc_free(root_map); 775 root_map = 0; 776#endif 777 return c; 778 } 779 return 0; 780} 781 782/* 783 * Error map 784 */ 785static int 786error_init(char *map, time_t *tp) 787{ 788 plog(XLOG_USER, "No source data for map %s", map); 789 *tp = 0; 790 return 0; 791} 792 793static int 794error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) 795{ 796 return ENOENT; 797} 798 799static int 800error_reload(mnt_map *m, char *map, add_fn *fn) 801{ 802 return ENOENT; 803} 804 805static int 806error_mtime(char *map, time_t *tp) 807{ 808 *tp = 0; 809 return 0; 810} 811