dict_open.c revision 1.2
1/* $NetBSD: dict_open.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3/*++ 4/* NAME 5/* dict_open 3 6/* SUMMARY 7/* low-level dictionary interface 8/* SYNOPSIS 9/* #include <dict.h> 10/* 11/* DICT *dict_open(dict_spec, open_flags, dict_flags) 12/* const char *dict_spec; 13/* int open_flags; 14/* int dict_flags; 15/* 16/* DICT *dict_open3(dict_type, dict_name, open_flags, dict_flags) 17/* const char *dict_type; 18/* const char *dict_name; 19/* int open_flags; 20/* int dict_flags; 21/* 22/* int dict_put(dict, key, value) 23/* DICT *dict; 24/* const char *key; 25/* const char *value; 26/* 27/* const char *dict_get(dict, key) 28/* DICT *dict; 29/* const char *key; 30/* 31/* int dict_del(dict, key) 32/* DICT *dict; 33/* const char *key; 34/* 35/* int dict_seq(dict, func, key, value) 36/* DICT *dict; 37/* int func; 38/* const char **key; 39/* const char **value; 40/* 41/* void dict_close(dict) 42/* DICT *dict; 43/* 44/* typedef DICT *(*DICT_OPEN_FN) (const char *, int, int); 45/* 46/* dict_open_register(type, open) 47/* const char *type; 48/* DICT_OPEN_FN open; 49/* 50/* typedef DICT_OPEN_FN (*DICT_OPEN_EXTEND_FN)(const char *type); 51/* 52/* DICT_OPEN_EXTEND_FN dict_open_extend(call_back) 53/* DICT_OPEN_EXTEND_FN call_back; 54/* 55/* ARGV *dict_mapnames() 56/* 57/* typedef ARGV *(*DICT_MAPNAMES_EXTEND_FN)(ARGV *names); 58/* 59/* DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(call_back) 60/* DICT_MAPNAMES_EXTEND_FN call_back; 61/* 62/* int dict_isjmp(dict) 63/* DICT *dict; 64/* 65/* int dict_setjmp(dict) 66/* DICT *dict; 67/* 68/* int dict_longjmp(dict, val) 69/* DICT *dict; 70/* int val; 71/* 72/* void dict_type_override(dict, type) 73/* DICT *dict; 74/* const char *type; 75/* DESCRIPTION 76/* This module implements a low-level interface to multiple 77/* physical dictionary types. 78/* 79/* dict_open() takes a type:name pair that specifies a dictionary type 80/* and dictionary name, opens the dictionary, and returns a dictionary 81/* handle. The \fIopen_flags\fR arguments are as in open(2). The 82/* \fIdict_flags\fR are the bit-wise OR of zero or more of the following: 83/* .IP DICT_FLAG_DUP_WARN 84/* Warn about duplicate keys, if the underlying database does not 85/* support duplicate keys. The default is to terminate with a fatal 86/* error. 87/* .IP DICT_FLAG_DUP_IGNORE 88/* Ignore duplicate keys if the underlying database does not 89/* support duplicate keys. The default is to terminate with a fatal 90/* error. 91/* .IP DICT_FLAG_DUP_REPLACE 92/* Replace duplicate keys if the underlying database supports such 93/* an operation. The default is to terminate with a fatal error. 94/* .IP DICT_FLAG_TRY0NULL 95/* With maps where this is appropriate, append no null byte to 96/* keys and values. 97/* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are 98/* specified, the software guesses what format to use for reading; 99/* and in the absence of definite information, a system-dependent 100/* default is chosen for writing. 101/* .IP DICT_FLAG_TRY1NULL 102/* With maps where this is appropriate, append one null byte to 103/* keys and values. 104/* When neither DICT_FLAG_TRY0NULL nor DICT_FLAG_TRY1NULL are 105/* specified, the software guesses what format to use for reading; 106/* and in the absence of definite information, a system-dependent 107/* default is chosen for writing. 108/* .IP DICT_FLAG_LOCK 109/* With maps where this is appropriate, acquire an exclusive lock 110/* before writing, and acquire a shared lock before reading. 111/* Release the lock when the operation completes. 112/* .IP DICT_FLAG_OPEN_LOCK 113/* The behavior of this flag depends on whether a database 114/* sets the DICT_FLAG_MULTI_WRITER flag to indicate that it 115/* is multi-writer safe. 116/* 117/* With databases that are not multi-writer safe, dict_open() 118/* acquires a persistent exclusive lock, or it terminates with 119/* a fatal run-time error. 120/* 121/* With databases that are multi-writer safe, dict_open() 122/* downgrades the DICT_FLAG_OPEN_LOCK flag (persistent lock) 123/* to DICT_FLAG_LOCK (temporary lock). 124/* .IP DICT_FLAG_FOLD_FIX 125/* With databases whose lookup fields are fixed-case strings, 126/* fold the search string to lower case before accessing the 127/* database. This includes hash:, cdb:, dbm:. nis:, ldap:, 128/* *sql. WARNING: case folding is supported only for ASCII or 129/* valid UTF-8. 130/* .IP DICT_FLAG_FOLD_MUL 131/* With databases where one lookup field can match both upper 132/* and lower case, fold the search key to lower case before 133/* accessing the database. This includes regexp: and pcre:. 134/* WARNING: case folding is supported only for ASCII or valid 135/* UTF-8. 136/* .IP DICT_FLAG_FOLD_ANY 137/* Short-hand for (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL). 138/* .IP DICT_FLAG_SYNC_UPDATE 139/* With file-based maps, flush I/O buffers to file after each update. 140/* Thus feature is not supported with some file-based dictionaries. 141/* .IP DICT_FLAG_NO_REGSUB 142/* Disallow regular expression substitution from the lookup string 143/* into the lookup result, to block data injection attacks. 144/* .IP DICT_FLAG_NO_PROXY 145/* Disallow access through the unprivileged \fBproxymap\fR 146/* service, to block privilege escalation attacks. 147/* .IP DICT_FLAG_NO_UNAUTH 148/* Disallow lookup mechanisms that lack any form of authentication, 149/* to block privilege escalation attacks (example: tcp_table; 150/* even NIS can be secured to some extent by requiring that 151/* the server binds to a privileged port). 152/* .IP DICT_FLAG_PARANOID 153/* A combination of all the paranoia flags: DICT_FLAG_NO_REGSUB, 154/* DICT_FLAG_NO_PROXY and DICT_FLAG_NO_UNAUTH. 155/* .IP DICT_FLAG_BULK_UPDATE 156/* Enable preliminary code for bulk-mode database updates. 157/* The caller must create an exception handler with dict_jmp_alloc() 158/* and must trap exceptions from the database client with dict_setjmp(). 159/* .IP DICT_FLAG_DEBUG 160/* Enable additional logging. 161/* .IP DICT_FLAG_UTF8_REQUEST 162/* With util_utf8_enable != 0, require that lookup/update/delete 163/* keys and values are valid UTF-8. Skip a lookup/update/delete 164/* request with a non-UTF-8 key, skip an update request with 165/* a non-UTF-8 value, and fail a lookup request with a non-UTF-8 166/* value. 167/* .PP 168/* Specify DICT_FLAG_NONE for no special processing. 169/* 170/* The dictionary types are as follows: 171/* .IP environ 172/* The process environment array. The \fIdict_name\fR argument is ignored. 173/* .IP dbm 174/* DBM file. 175/* .IP hash 176/* Berkeley DB file in hash format. 177/* .IP btree 178/* Berkeley DB file in btree format. 179/* .IP nis 180/* NIS map. Only read access is supported. 181/* .IP nisplus 182/* NIS+ map. Only read access is supported. 183/* .IP netinfo 184/* NetInfo table. Only read access is supported. 185/* .IP ldap 186/* LDAP ("light-weight" directory access protocol) database access. 187/* .IP pcre 188/* PERL-compatible regular expressions. 189/* .IP regexp 190/* POSIX-compatible regular expressions. 191/* .IP texthash 192/* Flat text in postmap(1) input format. 193/* .PP 194/* dict_open3() takes separate arguments for dictionary type and 195/* name, but otherwise performs the same functions as dict_open(). 196/* 197/* The dict_get(), dict_put(), dict_del(), and dict_seq() 198/* macros evaluate their first argument multiple times. 199/* These names should have been in uppercase. 200/* 201/* dict_get() retrieves the value stored in the named dictionary 202/* under the given key. A null pointer means the value was not found. 203/* As with dict_lookup(), the result is owned by the lookup table 204/* implementation. Make a copy if the result is to be modified, 205/* or if the result is to survive multiple table lookups. 206/* 207/* dict_put() stores the specified key and value into the named 208/* dictionary. A zero (DICT_STAT_SUCCESS) result means the 209/* update was made. 210/* 211/* dict_del() removes a dictionary entry, and returns 212/* DICT_STAT_SUCCESS in case of success. 213/* 214/* dict_seq() iterates over all members in the named dictionary. 215/* func is define DICT_SEQ_FUN_FIRST (select first member) or 216/* DICT_SEQ_FUN_NEXT (select next member). A zero (DICT_STAT_SUCCESS) 217/* result means that an entry was found. 218/* 219/* dict_close() closes the specified dictionary and cleans up the 220/* associated data structures. 221/* 222/* dict_open_register() adds support for a new dictionary type. 223/* 224/* dict_open_extend() registers a call-back function that looks 225/* up the dictionary open() function for a type that is not 226/* registered, or null in case of error. The result value is 227/* the last previously-registered call-back or null. 228/* 229/* dict_mapnames() returns a sorted list with the names of all available 230/* dictionary types. 231/* 232/* dict_mapnames_extend() registers a call-back function that 233/* enumerates additional dictionary type names. The result 234/* will be sorted by dict_mapnames(). The result value 235/* is the last previously-registered call-back or null. 236/* 237/* dict_setjmp() saves processing context and makes that context 238/* available for use with dict_longjmp(). Normally, dict_setjmp() 239/* returns zero. A non-zero result means that dict_setjmp() 240/* returned through a dict_longjmp() call; the result is the 241/* \fIval\fR argument given to dict_longjmp(). dict_isjmp() 242/* returns non-zero when dict_setjmp() and dict_longjmp() 243/* are enabled for a given dictionary. 244/* 245/* NB: non-local jumps such as dict_longjmp() are not safe for 246/* jumping out of any routine that manipulates DICT data. 247/* longjmp() like calls are best avoided in signal handlers. 248/* 249/* dict_type_override() changes the symbolic dictionary type. 250/* This is used by dictionaries whose internals are based on 251/* some other dictionary type. 252/* DIAGNOSTICS 253/* Fatal error: open error, unsupported dictionary type, attempt to 254/* update non-writable dictionary. 255/* 256/* The lookup routine returns non-null when the request is 257/* satisfied. The update, delete and sequence routines return 258/* zero (DICT_STAT_SUCCESS) when the request is satisfied. 259/* The dict->errno value is non-zero only when the last operation 260/* was not satisfied due to a dictionary access error. This 261/* can have the following values: 262/* .IP DICT_ERR_NONE(zero) 263/* There was no dictionary access error. For example, the 264/* request was satisfied, the requested information did not 265/* exist in the dictionary, or the information already existed 266/* when it should not exist (collision). 267/* .IP DICT_ERR_RETRY(<0) 268/* The dictionary was temporarily unavailable. This can happen 269/* with network-based services. 270/* .IP DICT_ERR_CONFIG(<0) 271/* The dictionary was unavailable due to a configuration error. 272/* .PP 273/* Generally, a program is expected to test the function result 274/* value for "success" first. If the operation was not successful, 275/* a program is expected to test for a non-zero dict->error 276/* status to distinguish between a data notfound/collision 277/* condition or a dictionary access error. 278/* LICENSE 279/* .ad 280/* .fi 281/* The Secure Mailer license must be distributed with this software. 282/* AUTHOR(S) 283/* Wietse Venema 284/* IBM T.J. Watson Research 285/* P.O. Box 704 286/* Yorktown Heights, NY 10598, USA 287/*--*/ 288 289/* System library. */ 290 291#include <sys_defs.h> 292#include <string.h> 293#include <stdlib.h> 294 295/* Utility library. */ 296 297#include <argv.h> 298#include <mymalloc.h> 299#include <msg.h> 300#include <dict.h> 301#include <dict_cdb.h> 302#include <dict_env.h> 303#include <dict_unix.h> 304#include <dict_tcp.h> 305#include <dict_sdbm.h> 306#include <dict_dbm.h> 307#include <dict_db.h> 308#include <dict_lmdb.h> 309#include <dict_nis.h> 310#include <dict_nisplus.h> 311#include <dict_ni.h> 312#include <dict_pcre.h> 313#include <dict_regexp.h> 314#include <dict_static.h> 315#include <dict_cidr.h> 316#include <dict_ht.h> 317#include <dict_thash.h> 318#include <dict_sockmap.h> 319#include <dict_fail.h> 320#include <dict_pipe.h> 321#include <dict_random.h> 322#include <dict_union.h> 323#include <dict_inline.h> 324#include <stringops.h> 325#include <split_at.h> 326#include <htable.h> 327#include <myflock.h> 328 329 /* 330 * lookup table for available map types. 331 */ 332typedef struct { 333 char *type; 334 DICT_OPEN_FN open; 335} DICT_OPEN_INFO; 336 337static const DICT_OPEN_INFO dict_open_info[] = { 338 DICT_TYPE_ENVIRON, dict_env_open, 339 DICT_TYPE_HT, dict_ht_open, 340 DICT_TYPE_UNIX, dict_unix_open, 341 DICT_TYPE_TCP, dict_tcp_open, 342#ifdef HAS_DBM 343 DICT_TYPE_DBM, dict_dbm_open, 344#endif 345#ifdef HAS_DB 346 DICT_TYPE_HASH, dict_hash_open, 347 DICT_TYPE_BTREE, dict_btree_open, 348#endif 349#ifdef HAS_NIS 350 DICT_TYPE_NIS, dict_nis_open, 351#endif 352#ifdef HAS_NISPLUS 353 DICT_TYPE_NISPLUS, dict_nisplus_open, 354#endif 355#ifdef HAS_NETINFO 356 DICT_TYPE_NETINFO, dict_ni_open, 357#endif 358#ifdef HAS_POSIX_REGEXP 359 DICT_TYPE_REGEXP, dict_regexp_open, 360#endif 361 DICT_TYPE_STATIC, dict_static_open, 362 DICT_TYPE_CIDR, dict_cidr_open, 363 DICT_TYPE_THASH, dict_thash_open, 364 DICT_TYPE_SOCKMAP, dict_sockmap_open, 365 DICT_TYPE_FAIL, dict_fail_open, 366 DICT_TYPE_PIPE, dict_pipe_open, 367 DICT_TYPE_RANDOM, dict_random_open, 368 DICT_TYPE_UNION, dict_union_open, 369 DICT_TYPE_INLINE, dict_inline_open, 370#ifndef USE_DYNAMIC_MAPS 371#ifdef HAS_PCRE 372 DICT_TYPE_PCRE, dict_pcre_open, 373#endif 374#ifdef HAS_CDB 375 DICT_TYPE_CDB, dict_cdb_open, 376#endif 377#ifdef HAS_SDBM 378 DICT_TYPE_SDBM, dict_sdbm_open, 379#endif 380#ifdef HAS_LMDB 381 DICT_TYPE_LMDB, dict_lmdb_open, 382#endif 383#endif /* !USE_DYNAMIC_MAPS */ 384 0, 385}; 386 387static HTABLE *dict_open_hash; 388 389 /* 390 * Extension hooks. 391 */ 392static DICT_OPEN_EXTEND_FN dict_open_extend_hook; 393static DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend_hook; 394 395 /* 396 * Workaround. 397 */ 398DEFINE_DICT_LMDB_MAP_SIZE; 399DEFINE_DICT_DB_CACHE_SIZE; 400 401/* dict_open_init - one-off initialization */ 402 403static void dict_open_init(void) 404{ 405 const char *myname = "dict_open_init"; 406 const DICT_OPEN_INFO *dp; 407 408 if (dict_open_hash != 0) 409 msg_panic("%s: multiple initialization", myname); 410 dict_open_hash = htable_create(10); 411 412 for (dp = dict_open_info; dp->type; dp++) 413 htable_enter(dict_open_hash, dp->type, (void *) dp); 414} 415 416/* dict_open - open dictionary */ 417 418DICT *dict_open(const char *dict_spec, int open_flags, int dict_flags) 419{ 420 char *saved_dict_spec = mystrdup(dict_spec); 421 char *dict_name; 422 DICT *dict; 423 424 if ((dict_name = split_at(saved_dict_spec, ':')) == 0) 425 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s\"", 426 dict_spec); 427 428 dict = dict_open3(saved_dict_spec, dict_name, open_flags, dict_flags); 429 myfree(saved_dict_spec); 430 return (dict); 431} 432 433 434/* dict_open3 - open dictionary */ 435 436DICT *dict_open3(const char *dict_type, const char *dict_name, 437 int open_flags, int dict_flags) 438{ 439 const char *myname = "dict_open"; 440 DICT_OPEN_INFO *dp; 441 DICT_OPEN_FN open_fn; 442 DICT *dict; 443 444 if (*dict_type == 0 || *dict_name == 0) 445 msg_fatal("open dictionary: expecting \"type:name\" form instead of \"%s:%s\"", 446 dict_type, dict_name); 447 if (dict_open_hash == 0) 448 dict_open_init(); 449 if ((dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type)) == 0) { 450 if (dict_open_extend_hook != 0 451 && (open_fn = dict_open_extend_hook(dict_type)) != 0) { 452 dict_open_register(dict_type, open_fn); 453 dp = (DICT_OPEN_INFO *) htable_find(dict_open_hash, dict_type); 454 } 455 if (dp == 0) 456 return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags, 457 "unsupported dictionary type: %s", dict_type)); 458 } 459 if ((dict = dp->open(dict_name, open_flags, dict_flags)) == 0) 460 return (dict_surrogate(dict_type, dict_name, open_flags, dict_flags, 461 "cannot open %s:%s: %m", dict_type, dict_name)); 462 if (msg_verbose) 463 msg_info("%s: %s:%s", myname, dict_type, dict_name); 464 /* XXX The choice between wait-for-lock or no-wait is hard-coded. */ 465 if (dict->flags & DICT_FLAG_OPEN_LOCK) { 466 if (dict->flags & DICT_FLAG_LOCK) 467 msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock", 468 myname, dict_type, dict_name); 469 /* Multi-writer safe map: downgrade persistent lock to temporary. */ 470 if (dict->flags & DICT_FLAG_MULTI_WRITER) { 471 dict->flags &= ~DICT_FLAG_OPEN_LOCK; 472 dict->flags |= DICT_FLAG_LOCK; 473 } 474 /* Multi-writer unsafe map: acquire exclusive lock or bust. */ 475 else if (dict->lock(dict, MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0) 476 msg_fatal("%s:%s: unable to get exclusive lock: %m", 477 dict_type, dict_name); 478 } 479 /* Last step: insert proxy for UTF-8 syntax checks and casefolding. */ 480 if ((dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0 481 && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags)) 482 dict = dict_utf8_activate(dict); 483 return (dict); 484} 485 486/* dict_open_register - register dictionary type */ 487 488void dict_open_register(const char *type, DICT_OPEN_FN open) 489{ 490 const char *myname = "dict_open_register"; 491 DICT_OPEN_INFO *dp; 492 HTABLE_INFO *ht; 493 494 if (dict_open_hash == 0) 495 dict_open_init(); 496 if (htable_find(dict_open_hash, type)) 497 msg_panic("%s: dictionary type exists: %s", myname, type); 498 dp = (DICT_OPEN_INFO *) mymalloc(sizeof(*dp)); 499 dp->open = open; 500 ht = htable_enter(dict_open_hash, type, (void *) dp); 501 dp->type = ht->key; 502} 503 504/* dict_open_extend - register alternate dictionary search routine */ 505 506DICT_OPEN_EXTEND_FN dict_open_extend(DICT_OPEN_EXTEND_FN new_cb) 507{ 508 DICT_OPEN_EXTEND_FN old_cb; 509 510 old_cb = dict_open_extend_hook; 511 dict_open_extend_hook = new_cb; 512 return (old_cb); 513} 514 515/* dict_sort_alpha_cpp - qsort() callback */ 516 517static int dict_sort_alpha_cpp(const void *a, const void *b) 518{ 519 return (strcmp(((char **) a)[0], ((char **) b)[0])); 520} 521 522/* dict_mapnames - return an ARGV of available map_names */ 523 524ARGV *dict_mapnames() 525{ 526 HTABLE_INFO **ht_info; 527 HTABLE_INFO **ht; 528 DICT_OPEN_INFO *dp; 529 ARGV *mapnames; 530 531 if (dict_open_hash == 0) 532 dict_open_init(); 533 mapnames = argv_alloc(dict_open_hash->used + 1); 534 for (ht_info = ht = htable_list(dict_open_hash); *ht; ht++) { 535 dp = (DICT_OPEN_INFO *) ht[0]->value; 536 argv_add(mapnames, dp->type, ARGV_END); 537 } 538 if (dict_mapnames_extend_hook != 0) 539 (void) dict_mapnames_extend_hook(mapnames); 540 qsort((void *) mapnames->argv, mapnames->argc, sizeof(mapnames->argv[0]), 541 dict_sort_alpha_cpp); 542 myfree((void *) ht_info); 543 argv_terminate(mapnames); 544 return mapnames; 545} 546 547/* dict_mapnames_extend - register alternate dictionary type list routine */ 548 549DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb) 550{ 551 DICT_MAPNAMES_EXTEND_FN old_cb; 552 553 old_cb = dict_mapnames_extend_hook; 554 dict_mapnames_extend_hook = new_cb; 555 return (old_cb); 556} 557 558/* dict_type_override - disguise a dictionary type */ 559 560void dict_type_override(DICT *dict, const char *type) 561{ 562 myfree(dict->type); 563 dict->type = mystrdup(type); 564} 565 566#ifdef TEST 567 568 /* 569 * Proof-of-concept test program. 570 */ 571int main(int argc, char **argv) 572{ 573 dict_test(argc, argv); 574 return (0); 575} 576 577#endif 578