xref: /netbsd-src/external/ibm-public/postfix/dist/src/postconf/postconf_dbms.c (revision d90047b5d07facf36e6c01dcc0bded8997ce9cc2)
1 /*	$NetBSD: postconf_dbms.c,v 1.3 2020/03/18 19:05:17 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 
101 static const char *pcf_ldap_suffixes[] = {
102 #include "pcf_ldap_suffixes.h"
103     0,
104 };
105 
106 /* See mysql_table(5). */
107 
108 static const char *pcf_mysql_suffixes[] = {
109 #include "pcf_mysql_suffixes.h"
110     0,
111 };
112 
113 /* See pgsql_table(5). */
114 
115 static const char *pcf_pgsql_suffixes[] = {
116 #include "pcf_pgsql_suffixes.h"
117     0,
118 };
119 
120 /* See sqlite_table(5). */
121 
122 static const char *pcf_sqlite_suffixes[] = {
123 #include "pcf_sqlite_suffixes.h"
124     0,
125 };
126 
127 /* See memcache_table(5). */
128 
129 static 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   */
137 typedef struct {
138     const char *db_type;
139     const char **db_suffixes;
140 } PCF_DBMS_INFO;
141 
142 static 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 
153 static 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 pameter 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 
219 static 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 
321 void    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