xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/dict_open.c (revision 230b95665bbd3a9d1a53658a36b1053f8382a519)
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