Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: mail_conf.c,v 1.5 2026/05/09 18:49:16 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	mail_conf 3
      6 /* SUMMARY
      7 /*	global configuration parameter management
      8 /* SYNOPSIS
      9 /*	#include <mail_conf.h>
     10 /*
     11 /*	void	mail_conf_read()
     12 /*
     13 /*	void	mail_conf_suck()
     14 /*
     15 /*	void	mail_conf_flush()
     16 /*
     17 /*	void	mail_conf_checkdir(config_dir)
     18 /*	const char *config_dir;
     19 /*
     20 /*	void	mail_conf_update(name, value)
     21 /*	const char *name;
     22 /*	const char *value;
     23 /*
     24 /*	const char *mail_conf_lookup(name)
     25 /*	const char *name;
     26 /*
     27 /*	const char *mail_conf_eval(string)
     28 /*	const char *string;
     29 /*
     30 /*	const char *mail_conf_eval_once(string)
     31 /*	const char *string;
     32 /*
     33 /*	const char *mail_conf_lookup_eval(name)
     34 /*	const char *name;
     35 /* DESCRIPTION
     36 /*	mail_conf_suck() reads the global Postfix configuration
     37 /*	file, and stores its values into a global configuration
     38 /*	dictionary. When the configuration directory name is not
     39 /*	trusted, this function requires that the directory name is
     40 /*	authorized with the alternate_config_directories or
     41 /*	multi_instance_directories setting in the default main.cf file.
     42 /*
     43 /*	This function requires that all configuration directory
     44 /*	override mechanisms set the MAIL_CONFIG environment variable,
     45 /*	even if the override was specified via the command line.
     46 /*	This reduces the number of pathways that need to be checked
     47 /*	for possible security attacks.
     48 /*
     49 /*	mail_conf_read() invokes mail_conf_suck() and assigns the values
     50 /*	to global variables by calling mail_params_init().
     51 /*
     52 /*	mail_conf_flush() discards the global configuration dictionary.
     53 /*	This is needed in programs that read main.cf multiple times, to
     54 /*	ensure that deleted parameter settings are handled properly.
     55 /*
     56 /*	mail_conf_checkdir() verifies that configuration directory
     57 /*	is authorized through settings in the default main.cf file,
     58 /*	and terminates the program if it is not.
     59 /*
     60 /*	The following routines are wrappers around the generic dictionary
     61 /*	access routines.
     62 /*
     63 /*	mail_conf_update() updates the named global parameter. This has
     64 /*	no effect on parameters whose value has already been looked up.
     65 /*	The update succeeds or the program terminates with fatal error.
     66 /*
     67 /*	mail_conf_lookup() looks up the value of the named parameter.
     68 /*	A null pointer result means the parameter was not found.
     69 /*	The result is volatile and should be copied if it is to be
     70 /*	used for any appreciable amount of time.
     71 /*
     72 /*	mail_conf_eval() recursively expands any $parameters in the
     73 /*	string argument. The result is volatile and should be copied
     74 /*	if it is to be used for any appreciable amount of time.
     75 /*
     76 /*	mail_conf_eval_once() non-recursively expands any $parameters
     77 /*	in the string argument. The result is volatile and should
     78 /*	be copied if it is to be used for any appreciable amount
     79 /*	of time.
     80 /*
     81 /*	mail_conf_lookup_eval() looks up the named parameter, and expands any
     82 /*	$parameters in the result. The result is volatile and should be
     83 /*	copied if it is to be used for any appreciable amount of time.
     84 /* DIAGNOSTICS
     85 /*	Fatal errors: malformed numerical value.
     86 /* ENVIRONMENT
     87 /*	MAIL_CONFIG, non-default configuration database
     88 /*	MAIL_VERBOSE, enable verbose mode
     89 /* FILES
     90 /*	/etc/postfix: default Postfix configuration directory.
     91 /* SEE ALSO
     92 /*	dict(3) generic dictionary manager
     93 /*	mail_conf_int(3) integer-valued parameters
     94 /*	mail_conf_str(3) string-valued parameters
     95 /* LICENSE
     96 /* .ad
     97 /* .fi
     98 /*	The Secure Mailer license must be distributed with this software.
     99 /* AUTHOR(S)
    100 /*	Wietse Venema
    101 /*	IBM T.J. Watson Research
    102 /*	P.O. Box 704
    103 /*	Yorktown Heights, NY 10598, USA
    104 /*
    105 /*	Wietse Venema
    106 /*	Google, Inc.
    107 /*	111 8th Avenue
    108 /*	New York, NY 10011, USA
    109 /*--*/
    110 
    111 /* System library. */
    112 
    113 #include <sys_defs.h>
    114 #include <unistd.h>
    115 #include <stdlib.h>
    116 #include <string.h>
    117 
    118 /* Utility library. */
    119 
    120 #include <msg.h>
    121 #include <mymalloc.h>
    122 #include <vstream.h>
    123 #include <vstring.h>
    124 #include <dict.h>
    125 #include <safe.h>
    126 #include <stringops.h>
    127 #include <readlline.h>
    128 
    129 /* Global library. */
    130 
    131 #include "mail_params.h"
    132 #include "mail_conf.h"
    133 
    134 /* mail_conf_checkdir - authorize non-default directory */
    135 
    136 void    mail_conf_checkdir(const char *config_dir)
    137 {
    138     VSTRING *buf;
    139     VSTREAM *fp;
    140     char   *path;
    141     char   *name;
    142     char   *value;
    143     char   *cp;
    144     int     found = 0;
    145 
    146     /*
    147      * If running set-[ug]id, require that a non-default configuration
    148      * directory name is blessed as a bona fide configuration directory in
    149      * the default main.cf file.
    150      */
    151     path = concatenate(DEF_CONFIG_DIR, "/", "main.cf", (char *) 0);
    152     if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0)
    153 	msg_fatal("open file %s: %m", path);
    154 
    155     buf = vstring_alloc(1);
    156     while (found == 0 && readlline(buf, fp, (int *) 0)) {
    157 	if (split_nameval(vstring_str(buf), &name, &value) == 0
    158 	    && (strcmp(name, VAR_CONFIG_DIRS) == 0
    159 		|| strcmp(name, VAR_MULTI_CONF_DIRS) == 0)) {
    160 	    while (found == 0 && (cp = mystrtok(&value, CHARS_COMMA_SP)) != 0)
    161 		if (strcmp(cp, config_dir) == 0)
    162 		    found = 1;
    163 	}
    164     }
    165     if (vstream_fclose(fp))
    166 	msg_fatal("read file %s: %m", path);
    167     vstring_free(buf);
    168 
    169     if (found == 0) {
    170 	msg_error("unauthorized configuration directory name: %s", config_dir);
    171 	msg_fatal("specify \"%s = %s\" or \"%s = %s\" in %s",
    172 		  VAR_CONFIG_DIRS, config_dir,
    173 		  VAR_MULTI_CONF_DIRS, config_dir, path);
    174     }
    175     myfree(path);
    176 }
    177 
    178 /* mail_conf_read - read global configuration file */
    179 
    180 void    mail_conf_read(void)
    181 {
    182     mail_conf_suck();
    183     mail_params_init();
    184 }
    185 
    186 /* mail_conf_suck - suck in the global configuration file */
    187 
    188 void    mail_conf_suck(void)
    189 {
    190     char   *config_dir;
    191     char   *path;
    192 
    193     /*
    194      * The code below requires that all configuration directory override
    195      * mechanisms set the CONF_ENV_PATH environment variable, even if the
    196      * override was specified via the command line. This reduces the number
    197      * of pathways that need to be checked for possible security attacks.
    198      *
    199      * Note: this code necessarily runs before cleanenv() can enforce the
    200      * import_environment scrubbing policy.
    201      */
    202 
    203     /*
    204      * Permit references to unknown configuration variable names. We rely on
    205      * a separate configuration checking tool to spot misspelled names and
    206      * other kinds of trouble. Enter the configuration directory into the
    207      * default dictionary.
    208      */
    209     if (var_config_dir)
    210 	myfree(var_config_dir);
    211     if ((config_dir = getenv(CONF_ENV_PATH)) == 0)
    212 	config_dir = DEF_CONFIG_DIR;
    213     var_config_dir = mystrdup(config_dir);
    214     set_mail_conf_str(VAR_CONFIG_DIR, var_config_dir);
    215 
    216     /*
    217      * If the configuration directory name comes from an untrusted source,
    218      * require that it is listed in the default main.cf file.
    219      */
    220     if (strcmp(var_config_dir, DEF_CONFIG_DIR) != 0	/* non-default */
    221 	&& unsafe())				/* untrusted env and cli */
    222 	mail_conf_checkdir(var_config_dir);
    223     path = concatenate(var_config_dir, "/", "main.cf", (char *) 0);
    224     if (dict_load_file_xt(CONFIG_DICT, path) == 0)
    225 	msg_fatal("open %s: %m", path);
    226     myfree(path);
    227 }
    228 
    229 /* mail_conf_flush - discard configuration dictionary */
    230 
    231 void    mail_conf_flush(void)
    232 {
    233     if (dict_handle(CONFIG_DICT) != 0)
    234 	dict_unregister(CONFIG_DICT);
    235 }
    236 
    237 /* mail_conf_eval - expand macros in string */
    238 
    239 const char *mail_conf_eval(const char *string)
    240 {
    241 #define RECURSIVE	1
    242 
    243     return (dict_eval(CONFIG_DICT, string, RECURSIVE));
    244 }
    245 
    246 /* mail_conf_eval_once - expand one level of macros in string */
    247 
    248 const char *mail_conf_eval_once(const char *string)
    249 {
    250 #define NONRECURSIVE	0
    251 
    252     return (dict_eval(CONFIG_DICT, string, NONRECURSIVE));
    253 }
    254 
    255 /* mail_conf_lookup - lookup named variable */
    256 
    257 const char *mail_conf_lookup(const char *name)
    258 {
    259     return (dict_lookup(CONFIG_DICT, name));
    260 }
    261 
    262 /* mail_conf_lookup_eval - expand named variable */
    263 
    264 const char *mail_conf_lookup_eval(const char *name)
    265 {
    266     const char *value;
    267 
    268 #define RECURSIVE	1
    269 
    270     if ((value = dict_lookup(CONFIG_DICT, name)) != 0)
    271 	value = dict_eval(CONFIG_DICT, value, RECURSIVE);
    272     return (value);
    273 }
    274 
    275 /* mail_conf_update - update parameter */
    276 
    277 void    mail_conf_update(const char *key, const char *value)
    278 {
    279     dict_update(CONFIG_DICT, key, value);
    280 }
    281