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