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