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