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