postconf_dbms.c revision 1.3
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 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 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 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