postconf_dbms.c revision 1.4
1/*	$NetBSD: postconf_dbms.c,v 1.4 2022/10/08 16:12:47 christos Exp $	*/
2
3/*++
4/* NAME
5/*	postconf_dbms 3
6/* SUMMARY
7/*	legacy support for database-defined main.cf parameter names
8/* SYNOPSIS
9/*	#include <postconf.h>
10/*
11/*	void	pcf_register_dbms_parameters(param_value, flag_parameter,
12/*					local_scope)
13/*	const char *param_value;
14/*	const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *);
15/*	PCF_MASTER_ENT *local_scope;
16/* DESCRIPTION
17/*	This module implements legacy support for database configuration
18/*	where main.cf parameter names are generated by prepending
19/*	the database name to a database-defined suffix.
20/*
21/*	Arguments:
22/* .IP param_value
23/*	A parameter value to be searched for "type:table" strings.
24/*	When a database type is found that supports legacy-style
25/*	configuration, the table name is combined with each of the
26/*	database-defined suffixes to generate candidate parameter
27/*	names for that database type; if the table name specifies
28/*	a client configuration file, that file is scanned for unused
29/*	parameter settings.
30/* .IP flag_parameter
31/*	A function that takes as arguments a candidate parameter
32/*	name, parameter flags, and a PCF_MASTER_ENT pointer.  The
33/*	function will flag the parameter as "used" if it has a
34/*	"name=value" entry in the local or global namespace.
35/* .IP local_scope
36/*	The local namespace.
37/* DIAGNOSTICS
38/*	No explicit diagnostics.
39/* LICENSE
40/* .ad
41/* .fi
42/*	The Secure Mailer license must be distributed with this software.
43/* AUTHOR(S)
44/*	Wietse Venema
45/*	IBM T.J. Watson Research
46/*	P.O. Box 704
47/*	Yorktown Heights, NY 10598, USA
48/*
49/*	Wietse Venema
50/*	Google, Inc.
51/*	111 8th Avenue
52/*	New York, NY 10011, USA
53/*--*/
54
55/* System library. */
56
57#include <sys_defs.h>
58#include <sys/stat.h>
59#include <errno.h>
60#include <string.h>
61
62/* Utility library. */
63
64#include <stringops.h>
65#include <split_at.h>
66#include <mac_expand.h>
67#include <dict.h>
68#include <msg.h>
69#include <mymalloc.h>
70
71/* Global library. */
72
73#include <mail_conf.h>
74#include <mail_params.h>
75#include <dict_ht.h>
76#include <dict_proxy.h>
77#include <dict_ldap.h>
78#include <dict_mysql.h>
79#include <dict_pgsql.h>
80#include <dict_sqlite.h>
81#include <dict_memcache.h>
82
83/* Application-specific. */
84
85#include <postconf.h>
86
87 /*
88  * SLMs.
89  */
90#define STR(x)	vstring_str(x)
91
92#ifdef LEGACY_DBMS_SUPPORT
93
94 /*
95  * The legacy database interface automagically instantiates a list of
96  * parameters by prepending the table name to database-specific suffixes.
97  */
98
99/* See ldap_table(5). */
100
101static const char *pcf_ldap_suffixes[] = {
102#include "pcf_ldap_suffixes.h"
103    0,
104};
105
106/* See mysql_table(5). */
107
108static const char *pcf_mysql_suffixes[] = {
109#include "pcf_mysql_suffixes.h"
110    0,
111};
112
113/* See pgsql_table(5). */
114
115static const char *pcf_pgsql_suffixes[] = {
116#include "pcf_pgsql_suffixes.h"
117    0,
118};
119
120/* See sqlite_table(5). */
121
122static const char *pcf_sqlite_suffixes[] = {
123#include "pcf_sqlite_suffixes.h"
124    0,
125};
126
127/* See memcache_table(5). */
128
129static const char *pcf_memcache_suffixes[] = {
130#include "pcf_memcache_suffixes.h"
131    0,
132};
133
134 /*
135  * Bundle up the database types and their suffix lists.
136  */
137typedef struct {
138    const char *db_type;
139    const char **db_suffixes;
140} PCF_DBMS_INFO;
141
142static const PCF_DBMS_INFO pcf_dbms_info[] = {
143    DICT_TYPE_LDAP, pcf_ldap_suffixes,
144    DICT_TYPE_MYSQL, pcf_mysql_suffixes,
145    DICT_TYPE_PGSQL, pcf_pgsql_suffixes,
146    DICT_TYPE_SQLITE, pcf_sqlite_suffixes,
147    DICT_TYPE_MEMCACHE, pcf_memcache_suffixes,
148    0,
149};
150
151/* pcf_check_dbms_client - look for unused names in client configuration */
152
153static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file)
154{
155    DICT   *dict;
156    VSTREAM *fp;
157    const char **cpp;
158    const char *name;
159    const char *value;
160    char   *dict_spec;
161    int     dir;
162
163    /*
164     * We read each database client configuration file into its own
165     * dictionary, and nag only the first time that a file is visited.
166     */
167    dict_spec = concatenate(dp->db_type, ":", cf_file, (char *) 0);
168    if ((dict = dict_handle(dict_spec)) == 0) {
169	struct stat st;
170
171	/*
172	 * Populate the dictionary with settings in this database client
173	 * configuration file. Don't die if a file can't be opened - some
174	 * files may contain passwords and should not be world-readable.
175	 * Note: dict_load_fp() nags about duplicate parameter settings.
176	 */
177	dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0);
178	dict_register(dict_spec, dict);
179	if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0) {
180	    if (errno != EACCES)
181		msg_warn("open \"%s\" configuration \"%s\": %m",
182			 dp->db_type, cf_file);
183	    myfree(dict_spec);
184	    return;
185	}
186	if (fstat(vstream_fileno(fp), &st) == 0 && !S_ISREG(st.st_mode)) {
187	    msg_warn("open \"%s\" configuration \"%s\": not a regular file",
188		     dp->db_type, cf_file);
189	    myfree(dict_spec);
190	    (void) vstream_fclose(fp);
191	    return;
192	}
193	dict_load_fp(dict_spec, fp);
194	if (vstream_fclose(fp)) {
195	    msg_warn("read \"%s\" configuration \"%s\": %m",
196		     dp->db_type, cf_file);
197	    myfree(dict_spec);
198	    return;
199	}
200
201	/*
202	 * Remove all known database client parameters from this dictionary,
203	 * then report the remaining ones as "unused". We use ad-hoc logging
204	 * code, because a database client parameter namespace is unlike the
205	 * parameter namespaces in main.cf or master.cf.
206	 */
207	for (cpp = dp->db_suffixes; *cpp; cpp++)
208	    (void) dict_del(dict, *cpp);
209	for (dir = DICT_SEQ_FUN_FIRST;
210	     dict->sequence(dict, dir, &name, &value) == DICT_STAT_SUCCESS;
211	     dir = DICT_SEQ_FUN_NEXT)
212	    msg_warn("%s: unused parameter: %s=%s", dict_spec, name, value);
213    }
214    myfree(dict_spec);
215}
216
217/* pcf_register_dbms_helper - parse one possible database type:name */
218
219static void pcf_register_dbms_helper(char *str_value,
220         const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
221				             PCF_MASTER_ENT *local_scope)
222{
223    const PCF_DBMS_INFO *dp;
224    char   *db_type;
225    char   *prefix;
226    static VSTRING *candidate = 0;
227    const char **cpp;
228    char   *err;
229
230    /*
231     * Naive parsing. We don't really know if this substring specifies a
232     * database or some other text.
233     */
234    while ((db_type = mystrtokq(&str_value, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
235	if (*db_type == CHARS_BRACE[0]) {
236	    if ((err = extpar(&db_type, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) {
237		/* XXX Encapsulate this in pcf_warn() function. */
238		if (local_scope)
239		    msg_warn("%s:%s: %s",
240			     MASTER_CONF_FILE, local_scope->name_space, err);
241		else
242		    msg_warn("%s: %s", MAIN_CONF_FILE, err);
243		myfree(err);
244	    }
245	    pcf_register_dbms_helper(db_type, flag_parameter, local_scope);
246	    continue;
247	}
248
249	/*
250	 * Skip over "proxy:" maptypes, to emulate the proxymap(8) server's
251	 * behavior when opening a local database configuration file.
252	 */
253	while ((prefix = split_at(db_type, ':')) != 0
254	       && strcmp(db_type, DICT_TYPE_PROXY) == 0)
255	    db_type = prefix;
256
257	if (prefix == 0)
258	    continue;
259
260	/*
261	 * Look for database:prefix where the prefix is an absolute pathname.
262	 * Then, report unknown database client configuration parameters.
263	 *
264	 * XXX What about a pathname beginning with '.'? This supposedly is
265	 * relative to the queue directory, which is the default directory
266	 * for all Postfix daemon processes. This would also have to handle
267	 * the case that the queue is not yet created.
268	 */
269	if (*prefix == '/') {
270	    for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
271		if (strcmp(db_type, dp->db_type) == 0) {
272		    pcf_check_dbms_client(dp, prefix);
273		    break;
274		}
275	    }
276	    continue;
277	}
278
279	/*
280	 * Look for database:prefix where the prefix is not a pathname and
281	 * the database is a known type. Synthesize candidate parameter names
282	 * from the user-defined prefix and from the database-defined suffix
283	 * list, and see if those parameters have a "name=value" entry in the
284	 * local or global namespace.
285	 */
286	if (*prefix != '.') {
287	    if (*prefix == CHARS_BRACE[0]) {
288		if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) {
289		    /* XXX Encapsulate this in pcf_warn() function. */
290		    if (local_scope)
291			msg_warn("%s:%s: %s",
292				 MASTER_CONF_FILE, local_scope->name_space,
293				 err);
294		    else
295			msg_warn("%s: %s", MAIN_CONF_FILE, err);
296		    myfree(err);
297		}
298		pcf_register_dbms_helper(prefix, flag_parameter, local_scope);
299		continue;
300	    } else {
301		for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
302		    if (strcmp(db_type, dp->db_type) == 0) {
303			for (cpp = dp->db_suffixes; *cpp; cpp++) {
304			    vstring_sprintf(candidate ? candidate :
305					    (candidate = vstring_alloc(30)),
306					    "%s_%s", prefix, *cpp);
307			    flag_parameter(STR(candidate),
308				  PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER,
309					   local_scope);
310			}
311			break;
312		    }
313		}
314	    }
315	}
316    }
317}
318
319/* pcf_register_dbms_parameters - look for database_type:prefix_name */
320
321void    pcf_register_dbms_parameters(const char *param_value,
322         const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
323				             PCF_MASTER_ENT *local_scope)
324{
325    char   *bufp;
326    static VSTRING *buffer = 0;
327
328    /*
329     * XXX This does not examine both sides of conditional macro expansion,
330     * and may expand the "wrong" conditional macros. This is the best we can
331     * do for legacy database configuration support.
332     */
333    if (buffer == 0)
334	buffer = vstring_alloc(100);
335    bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value,
336				      local_scope);
337    pcf_register_dbms_helper(bufp, flag_parameter, local_scope);
338}
339
340#endif
341