1 /* $NetBSD: postconf_user.c,v 1.1.1.3 2014/07/06 19:27:53 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* postconf_user 3 6 /* SUMMARY 7 /* support for user-defined main.cf parameter names 8 /* SYNOPSIS 9 /* #include <postconf.h> 10 /* 11 /* void pcf_register_user_parameters() 12 /* DESCRIPTION 13 /* Postfix has multiple parameter name spaces: the global 14 /* main.cf parameter name space, and the local parameter name 15 /* space of each master.cf entry. Parameters in local name 16 /* spaces take precedence over global parameters. 17 /* 18 /* There are three categories of known parameter names: built-in, 19 /* service-defined (see postconf_service.c), and valid 20 /* user-defined. 21 /* 22 /* There are two categories of valid user-defined parameter 23 /* names: 24 /* 25 /* - Parameters whose user-defined-name appears in the value 26 /* of smtpd_restriction_classes in main.cf or master.cf. 27 /* 28 /* - Parameters whose $user-defined-name appear in the value 29 /* of "name=value" entries in main.cf or master.cf. 30 /* 31 /* - In both cases the parameters must have a 32 /* "user-defined-name=value" entry in main.cf or master.cf. 33 /* 34 /* Other user-defined parameter names are flagged as "unused". 35 /* 36 /* pcf_register_user_parameters() scans the global and per-service 37 /* name spaces for user-defined parameters and flags parameters 38 /* as "valid" in the global name space (pcf_param_table) or 39 /* in the per-service name space (valid_params). 40 /* DIAGNOSTICS 41 /* Problems are reported to the standard error stream. 42 /* LICENSE 43 /* .ad 44 /* .fi 45 /* The Secure Mailer license must be distributed with this software. 46 /* AUTHOR(S) 47 /* Wietse Venema 48 /* IBM T.J. Watson Research 49 /* P.O. Box 704 50 /* Yorktown Heights, NY 10598, USA 51 /*--*/ 52 53 /* System library. */ 54 55 #include <sys_defs.h> 56 #include <string.h> 57 58 /* Utility library. */ 59 60 #include <msg.h> 61 #include <mymalloc.h> 62 #include <htable.h> 63 #include <mac_expand.h> 64 #include <stringops.h> 65 66 /* Global library. */ 67 68 #include <mail_conf.h> 69 #include <mail_params.h> 70 71 /* Application-specific. */ 72 73 #include <postconf.h> 74 75 /* 76 * Hash with all user-defined names in the global smtpd_restriction_classes 77 * value. This is used when validating "-o user-defined-name=value" entries 78 * in master.cf. 79 */ 80 static HTABLE *pcf_rest_class_table; 81 82 /* 83 * SLMs. 84 */ 85 #define STR(x) vstring_str(x) 86 87 /* 88 * Macros to make code with obscure constants more readable. 89 */ 90 #define NO_SCAN_RESULT ((VSTRING *) 0) 91 #define NO_SCAN_FILTER ((char *) 0) 92 93 /* SCAN_USER_PARAMETER_VALUE - examine macro names in parameter value */ 94 95 #define SCAN_USER_PARAMETER_VALUE(value, class, scope) do { \ 96 PCF_PARAM_CTX _ctx; \ 97 _ctx.local_scope = (scope); \ 98 _ctx.param_class = (class); \ 99 (void) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \ 100 NO_SCAN_FILTER, pcf_flag_user_parameter_wrapper, (char *) &_ctx); \ 101 } while (0) 102 103 /* pcf_convert_user_parameter - get user-defined parameter string value */ 104 105 static const char *pcf_convert_user_parameter(char *unused_ptr) 106 { 107 return (""); /* can't happen */ 108 } 109 110 /* pcf_flag_user_parameter - flag user-defined name "valid" if it has name=value */ 111 112 static const char *pcf_flag_user_parameter(const char *mac_name, 113 int param_class, 114 PCF_MASTER_ENT *local_scope) 115 { 116 const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE; 117 int user_supplied = 0; 118 119 /* 120 * If the name=value exists in the local (or global) name space, update 121 * the local (or global) "valid" parameter name table. 122 * 123 * Do not "validate" user-defined parameters whose name appears only as 124 * macro expansion; this is how Postfix historically implements backwards 125 * compatibility after a feature name change. 126 */ 127 if (local_scope && dict_get(local_scope->all_params, mac_name)) { 128 user_supplied = 1; 129 /* $name in master.cf references name=value in master.cf. */ 130 if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0) { 131 PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name, 132 param_class, PCF_PARAM_NO_DATA, 133 pcf_convert_user_parameter); 134 if (msg_verbose) 135 msg_info("$%s in %s:%s validates %s=value in %s:%s", 136 mac_name, MASTER_CONF_FILE, 137 local_scope->name_space, 138 mac_name, MASTER_CONF_FILE, 139 local_scope->name_space); 140 } 141 } else if (mail_conf_lookup(mac_name) != 0) { 142 user_supplied = 1; 143 /* $name in main/master.cf references name=value in main.cf. */ 144 if (PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0) { 145 PCF_PARAM_TABLE_ENTER(pcf_param_table, mac_name, param_class, 146 PCF_PARAM_NO_DATA, pcf_convert_user_parameter); 147 if (msg_verbose) { 148 if (local_scope) 149 msg_info("$%s in %s:%s validates %s=value in %s", 150 mac_name, MASTER_CONF_FILE, 151 local_scope->name_space, 152 mac_name, MAIN_CONF_FILE); 153 else 154 msg_info("$%s in %s validates %s=value in %s", 155 mac_name, MAIN_CONF_FILE, 156 mac_name, MAIN_CONF_FILE); 157 } 158 } 159 } 160 if (local_scope == 0) { 161 for (local_scope = pcf_master_table; local_scope->argv; local_scope++) { 162 if (local_scope->all_params != 0 163 && dict_get(local_scope->all_params, mac_name) != 0) { 164 user_supplied = 1; 165 /* $name in main.cf references name=value in master.cf. */ 166 if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0) { 167 PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name, 168 param_class, PCF_PARAM_NO_DATA, 169 pcf_convert_user_parameter); 170 if (msg_verbose) 171 msg_info("$%s in %s validates %s=value in %s:%s", 172 mac_name, MAIN_CONF_FILE, 173 mac_name, MASTER_CONF_FILE, 174 local_scope->name_space); 175 } 176 } 177 } 178 } 179 180 /* 181 * Warn about a $name that has no user-supplied explicit value or 182 * Postfix-supplied default value. We don't enforce this for legacy DBMS 183 * parameters because they exist only for backwards compatibility, so we 184 * don't bother to figure out which parameters come without defaults. 185 */ 186 if (user_supplied == 0 && (param_class & PCF_PARAM_FLAG_DBMS) == 0 187 && PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0) 188 msg_warn("%s/%s: undefined parameter: %s", 189 var_config_dir, source, mac_name); 190 return (0); 191 } 192 193 /* pcf_flag_user_parameter_wrapper - mac_expand call-back helper */ 194 195 static const char *pcf_flag_user_parameter_wrapper(const char *mac_name, 196 int unused_mode, 197 char *context) 198 { 199 PCF_PARAM_CTX *ctx = (PCF_PARAM_CTX *) context; 200 201 return (pcf_flag_user_parameter(mac_name, ctx->param_class, ctx->local_scope)); 202 } 203 204 /* pcf_lookup_eval - generalized mail_conf_lookup_eval */ 205 206 static const char *pcf_lookup_eval(const char *dict_name, const char *name) 207 { 208 const char *value; 209 210 #define RECURSIVE 1 211 212 if ((value = dict_lookup(dict_name, name)) != 0) 213 value = dict_eval(dict_name, value, RECURSIVE); 214 return (value); 215 } 216 217 /* pcf_scan_user_parameter_namespace - scan parameters in name space */ 218 219 static void pcf_scan_user_parameter_namespace(const char *dict_name, 220 PCF_MASTER_ENT *local_scope) 221 { 222 const char *myname = "pcf_scan_user_parameter_namespace"; 223 const char *class_list; 224 char *saved_class_list; 225 char *cp; 226 DICT *dict; 227 char *param_name; 228 int how; 229 const char *cparam_name; 230 const char *cparam_value; 231 PCF_PARAM_NODE *node; 232 const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE; 233 234 /* 235 * Flag parameter names in smtpd_restriction_classes as "valid", but only 236 * if they have a "name=value" entry. If we are in not in a local name 237 * space, update the global restriction class name table, so that we can 238 * query the global table from within a local master.cf name space. 239 */ 240 if ((class_list = pcf_lookup_eval(dict_name, VAR_REST_CLASSES)) != 0) { 241 cp = saved_class_list = mystrdup(class_list); 242 while ((param_name = mystrtok(&cp, ", \t\r\n")) != 0) { 243 if (local_scope == 0 244 && htable_locate(pcf_rest_class_table, param_name) == 0) 245 htable_enter(pcf_rest_class_table, param_name, ""); 246 pcf_flag_user_parameter(param_name, PCF_PARAM_FLAG_USER, local_scope); 247 } 248 myfree(saved_class_list); 249 } 250 251 /* 252 * For all "name=value" instances: a) if the name space is local and the 253 * name appears in the global restriction class table, flag the name as 254 * "valid" in the local name space; b) scan the value for macro 255 * expansions of unknown parameter names, and flag those parameter names 256 * as "valid" if they have a "name=value" entry. 257 * 258 * We delete name=value entries for read-only parameters, to maintain 259 * compatibility with Postfix programs that ignore such settings. 260 */ 261 if ((dict = dict_handle(dict_name)) == 0) 262 msg_panic("%s: parameter dictionary %s not found", 263 myname, dict_name); 264 if (dict->sequence == 0) 265 msg_panic("%s: parameter dictionary %s has no iterator", 266 myname, dict_name); 267 for (how = DICT_SEQ_FUN_FIRST; 268 dict->sequence(dict, how, &cparam_name, &cparam_value) == 0; 269 how = DICT_SEQ_FUN_NEXT) { 270 if (local_scope != 0 271 && PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, cparam_name) == 0 272 && htable_locate(pcf_rest_class_table, cparam_name) != 0) 273 PCF_PARAM_TABLE_ENTER(local_scope->valid_names, cparam_name, 274 PCF_PARAM_FLAG_USER, PCF_PARAM_NO_DATA, 275 pcf_convert_user_parameter); 276 if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, cparam_name)) != 0) { 277 if (PCF_READONLY_PARAMETER(node)) { 278 msg_warn("%s/%s: read-only parameter assignment: %s=%s", 279 var_config_dir, source, cparam_name, cparam_value); 280 /* Can't use dict_del() with Postfix<2.10 htable_sequence(). */ 281 if (dict_del(dict, cparam_name) != 0) 282 msg_panic("%s: can't delete %s/%s parameter entry for %s", 283 myname, var_config_dir, source, cparam_name); 284 continue; 285 } 286 /* Re-label legacy parameter as user-defined, so it's printed. */ 287 if (PCF_LEGACY_PARAMETER(node)) 288 PCF_PARAM_CLASS_OVERRIDE(node, PCF_PARAM_FLAG_USER); 289 /* Skip "do not expand" parameters. */ 290 if (PCF_RAW_PARAMETER(node)) 291 continue; 292 } 293 SCAN_USER_PARAMETER_VALUE(cparam_value, PCF_PARAM_FLAG_USER, local_scope); 294 #ifdef LEGACY_DBMS_SUPPORT 295 pcf_register_dbms_parameters(cparam_value, pcf_flag_user_parameter, 296 local_scope); 297 #endif 298 } 299 } 300 301 /* pcf_scan_default_parameter_values - scan parameters at implicit defaults */ 302 303 static void pcf_scan_default_parameter_values(HTABLE *valid_params, 304 const char *dict_name, 305 PCF_MASTER_ENT *local_scope) 306 { 307 const char *myname = "pcf_scan_default_parameter_values"; 308 PCF_PARAM_INFO **list; 309 PCF_PARAM_INFO **ht; 310 const char *param_value; 311 312 list = PCF_PARAM_TABLE_LIST(valid_params); 313 for (ht = list; *ht; ht++) { 314 /* Skip "do not expand" parameters. */ 315 if (PCF_RAW_PARAMETER(PCF_PARAM_INFO_NODE(*ht))) 316 continue; 317 /* Skip parameters with a non-default value. */ 318 if (dict_lookup(dict_name, PCF_PARAM_INFO_NAME(*ht))) 319 continue; 320 if ((param_value = pcf_convert_param_node(PCF_SHOW_DEFS, PCF_PARAM_INFO_NAME(*ht), 321 PCF_PARAM_INFO_NODE(*ht))) == 0) 322 msg_panic("%s: parameter %s has no default value", 323 myname, PCF_PARAM_INFO_NAME(*ht)); 324 SCAN_USER_PARAMETER_VALUE(param_value, PCF_PARAM_FLAG_USER, local_scope); 325 /* No need to scan default values for legacy DBMS configuration. */ 326 } 327 myfree((char *) list); 328 } 329 330 /* pcf_register_user_parameters - add parameters with user-defined names */ 331 332 void pcf_register_user_parameters(void) 333 { 334 const char *myname = "pcf_register_user_parameters"; 335 PCF_MASTER_ENT *masterp; 336 ARGV *argv; 337 char *arg; 338 char *aval; 339 int field; 340 char *saved_arg; 341 char *param_name; 342 char *param_value; 343 DICT *dict; 344 345 /* 346 * Sanity checks. 347 */ 348 if (pcf_param_table == 0) 349 msg_panic("%s: global parameter table is not initialized", myname); 350 if (pcf_master_table == 0) 351 msg_panic("%s: master table is not initialized", myname); 352 if (pcf_rest_class_table != 0) 353 msg_panic("%s: restriction class table is already initialized", myname); 354 355 /* 356 * Initialize the table with global restriction class names. 357 */ 358 pcf_rest_class_table = htable_create(1); 359 360 /* 361 * Initialize the per-service parameter name spaces. 362 */ 363 for (masterp = pcf_master_table; (argv = masterp->argv) != 0; masterp++) { 364 for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) { 365 arg = argv->argv[field]; 366 if (arg[0] != '-' || strcmp(arg, "--") == 0) 367 break; 368 if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0 369 || (aval = argv->argv[field + 1]) == 0) 370 continue; 371 if (strcmp(arg, "-o") == 0) { 372 saved_arg = mystrdup(aval); 373 if (split_nameval(saved_arg, ¶m_name, ¶m_value) == 0) 374 dict_update(masterp->name_space, param_name, param_value); 375 myfree(saved_arg); 376 } 377 field += 1; 378 } 379 if ((dict = dict_handle(masterp->name_space)) != 0) { 380 masterp->all_params = dict; 381 masterp->valid_names = htable_create(1); 382 } 383 } 384 385 /* 386 * Scan the "-o parameter=value" instances in each master.cf name space. 387 */ 388 for (masterp = pcf_master_table; masterp->argv != 0; masterp++) 389 if (masterp->all_params != 0) 390 pcf_scan_user_parameter_namespace(masterp->name_space, masterp); 391 392 /* 393 * Scan parameter values that are left at their defaults in the global 394 * name space. Some defaults contain the $name of an obsolete parameter 395 * for backwards compatilility purposes. We might warn that an explicit 396 * name=value is obsolete, but we must not warn that the parameter is 397 * unused. 398 */ 399 pcf_scan_default_parameter_values(pcf_param_table, CONFIG_DICT, 400 (PCF_MASTER_ENT *) 0); 401 402 /* 403 * Scan the explicit name=value entries in the global name space. 404 */ 405 pcf_scan_user_parameter_namespace(CONFIG_DICT, (PCF_MASTER_ENT *) 0); 406 } 407