1 /*        $NetBSD: mail_conf.c,v 1.4 2025/02/25 19:15:45 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 setting
41 /*        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 
mail_conf_checkdir(const char * config_dir)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 
mail_conf_read(void)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 
mail_conf_suck(void)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 
mail_conf_flush(void)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 
mail_conf_eval(const char * string)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 
mail_conf_eval_once(const char * string)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 
mail_conf_lookup(const char * name)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 
mail_conf_lookup_eval(const char * name)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 
mail_conf_update(const char * key,const char * value)277 void    mail_conf_update(const char *key, const char *value)
278 {
279     dict_update(CONFIG_DICT, key, value);
280 }
281