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