1/*	$NetBSD: mail_conf.c,v 1.3 2020/03/18 19:05: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 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
136void 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
180void    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
188void    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
231void    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
239const 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
248const 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
257const 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
264const 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
277void    mail_conf_update(const char *key, const char *value)
278{
279    dict_update(CONFIG_DICT, key, value);
280}
281