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 */
52 static 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
70 static 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 */
92 typedef void add_fn(mnt_map *, char *, char *);
93 typedef int init_fn(char *, time_t *);
94 typedef int search_fn(mnt_map *, char *, char *, char **, time_t *);
95 typedef int reload_fn(mnt_map *, char *, add_fn *);
96 typedef int mtime_fn(char *, time_t *);
97
98 static void mapc_sync(mnt_map *);
99
100 /*
101 * Map type
102 */
103 typedef struct map_type map_type;
104 struct 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 */
116 typedef struct kv kv;
117 struct kv {
118 kv *next;
119 char *key;
120 char *val;
121 };
122
123 struct 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 */
140 static mnt_map *root_map;
141
142 /*
143 * List of known maps
144 */
145 extern qelem map_list_head;
146 qelem map_list_head = { &map_list_head, &map_list_head };
147
148 /*
149 * Configuration
150 */
151
152 /* ROOT MAP */
153 static int root_init(char *, time_t *);
154
155 /* FILE MAPS */
156 extern int file_init(char *, time_t *);
157 extern int file_reload(mnt_map *, char *, add_fn *);
158 extern int file_search(mnt_map *, char *, char *, char **, time_t *);
159 extern int file_mtime(char *, time_t *);
160
161 /* Network Information Service (NIS) MAPS */
162 extern int nis_init(char *, time_t *);
163 extern int nis_reload(mnt_map *, char *, add_fn *);
164 extern 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
169 extern int ndbm_init(char *, time_t *);
170 extern 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 */
175 extern int passwd_init(char *, time_t *);
176 extern int passwd_search(mnt_map *, char *, char *, char **, time_t *);
177
178 /* UNION MAPS */
179 extern int union_init(char *, time_t *);
180 extern int union_search(mnt_map *, char *, char *, char **, time_t *);
181 extern int union_reload(mnt_map *, char *, add_fn *);
182
183 /* ERROR MAP */
184 static int error_init(char *, time_t *);
185 static int error_reload(mnt_map *, char *, add_fn *);
186 static int error_search(mnt_map *, char *, char *, char **, time_t *);
187 static int error_mtime(char *, time_t *);
188
189 static 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 */
210 static unsigned int
kvhash_of(char * key)211 kvhash_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
221 void
mapc_showtypes(FILE * fp)222 mapc_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 */
237 void
mapc_add_kv(mnt_map * m,char * key,char * val)238 mapc_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
285 static void
mapc_repl_kv(mnt_map * m,char * key,char * val)286 mapc_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 */
search_map(mnt_map * m,char * key,char ** valp)315 static 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 */
334 static void
mapc_find_wildcard(mnt_map * m)335 mapc_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 */
354 static int
mapc_reload_map(mnt_map * m)355 mapc_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 */
377 static mnt_map *
mapc_create(char * map,char * opt)378 mapc_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 */
450 static void
mapc_clear(mnt_map * m)451 mapc_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 */
485 mnt_map *
mapc_find(char * map,char * opt)486 mapc_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 */
509 void
mapc_free(void * arg)510 mapc_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 */
531 int
mapc_meta_search(mnt_map * m,char * key,char ** pval,int recurse)532 mapc_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
654 int
mapc_search(mnt_map * m,char * key,char ** pval)655 mapc_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 */
663 static void
mapc_sync(mnt_map * m)664 mapc_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 */
mapc_reload(void)684 void 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
707 static int
root_init(char * map,time_t * tp)708 root_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 */
721 void
root_newmap(char * dir,char * opts,char * map)722 root_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
744 int
mapc_keyiter(mnt_map * m,void (* fn)(char *,void *),void * arg)745 mapc_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 */
768 int
root_keyiter(void (* fn)(char *,void *),void * arg)769 root_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 */
785 static int
error_init(char * map,time_t * tp)786 error_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
793 static int
error_search(mnt_map * m,char * map,char * key,char ** pval,time_t * tp)794 error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
795 {
796 return ENOENT;
797 }
798
799 static int
error_reload(mnt_map * m,char * map,add_fn * fn)800 error_reload(mnt_map *m, char *map, add_fn *fn)
801 {
802 return ENOENT;
803 }
804
805 static int
error_mtime(char * map,time_t * tp)806 error_mtime(char *map, time_t *tp)
807 {
808 *tp = 0;
809 return 0;
810 }
811