1 /* $Id: conf.c,v 1.74 2014/02/04 15:19:25 manu Exp $ */
2 
3 /*
4  * Copyright (c) 2004-2010 Emmanuel Dreyfus
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by Emmanuel Dreyfus
18  *
19  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
20  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29  * OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 
34 #ifdef HAVE_SYS_CDEFS_H
35 #include <sys/cdefs.h>
36 #ifdef __RCSID
37 __RCSID("$Id: conf.c,v 1.74 2014/02/04 15:19:25 manu Exp $");
38 #endif
39 #endif
40 
41 #if defined(HAVE_OLD_QUEUE_H) || !defined(HAVE_SYS_QUEUE_H)
42 #include "queue.h"
43 #else
44 #include <sys/queue.h>
45 #endif
46 
47 #include <errno.h>
48 #include <stdio.h>
49 #include <ctype.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <strings.h>
53 #include <syslog.h>
54 #include <pthread.h>
55 #include <sysexits.h>
56 #include <sys/socket.h>
57 #include <sys/stat.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <assert.h>
61 #include <time.h>
62 #include <sys/time.h>
63 
64 #include "spf.h"
65 #include "acl.h"
66 #ifdef USE_DNSRBL
67 #include "dnsrbl.h"
68 #endif
69 #ifdef USE_CURL
70 #include "urlcheck.h"
71 #endif
72 #ifdef USE_LDAP
73 #include "ldapcheck.h"
74 #endif
75 #ifdef USE_DKIM
76 #include "dkimcheck.h"
77 #endif
78 #ifdef USE_P0F
79 #include "p0f.h"
80 #endif
81 #ifdef USE_SPAMD
82 #include "spamd.h"
83 #endif
84 #include "conf.h"
85 #include "sync.h"
86 #include "pending.h"
87 #include "dump.h"
88 #include "list.h"
89 #include "macro.h"
90 #include "ratelimit.h"
91 #include "milter-greylist.h"
92 
93 #ifdef USE_DMALLOC
94 #include <dmalloc.h>
95 #endif
96 
97 /* #define CONF_DEBUG */
98 
99 /* Default configuration */
100 struct conf_rec defconf;
101 pthread_key_t conf_key;
102 char *conffile = CONFFILE;
103 int conf_cold = 1;
104 int conf_specified = 0;
105 
106 #define CONF_LOCK pthread_mutex_lock(&conf_lock);
107 #define CONF_UNLOCK pthread_mutex_unlock(&conf_lock);
108 static pthread_mutex_t conf_lock = PTHREAD_MUTEX_INITIALIZER;
109 static struct conf_list conf_list_head;
110 static pthread_cond_t conf_update_cond = PTHREAD_COND_INITIALIZER;
111 static int conf_updating;
112 static char *legacy_conffile = "/etc/mail/greylist.conf";
113 
114 void
conf_init(void)115 conf_init(void) {
116 	int error;
117 
118 	TAILQ_INIT(&conf_list_head);
119 
120 	if ((error = pthread_key_create(&conf_key, 0)) != 0) {
121 		mg_log(LOG_ERR,
122 		    "pthread_key_create failed: %s", strerror(error));
123 		exit(EX_OSERR);
124 	}
125 
126 	return;
127 }
128 
129 #ifdef CONF_DEBUG
130 static void
conf_dump(void)131 conf_dump(void) {
132 	struct conf_rec *c;
133 
134 	TAILQ_FOREACH_REVERSE(c, &conf_list_head, conf_list, c_chain) {
135 		char textdate[DATELEN];
136 		struct tm tm;
137 
138 		localtime_r(&c->c_timestamp, &tm);
139 		strftime(textdate, sizeof textdate, "%Y-%m-%d %T",  &tm);
140 		mg_log(LOG_DEBUG, "conf_dump: stamp %s ref %d",
141 			   textdate, c->c_refcount);
142 	}
143 }
144 #endif /* CONF_DEBUG */
145 
146 static void *
conf_load_internal(timestamp)147 conf_load_internal(timestamp)
148 	void *timestamp;
149 {
150 	FILE *stream;
151 	struct timeval tv1, tv2, tv3;
152 	struct conf_rec *currconf, *threadconf, *newconf;
153 
154 	CONF_LOCK;
155 	currconf = TAILQ_FIRST(&conf_list_head);
156 	CONF_UNLOCK;
157 	assert(conf_cold ? (currconf == NULL) : (currconf != NULL));
158 	threadconf = GET_CONF();
159 
160 	if (!(newconf = (struct conf_rec *)malloc(sizeof *newconf))) {
161 		mg_log(LOG_ERR, "conf malloc failed: %s", strerror(errno));
162 		exit(EX_OSERR);
163 	}
164 
165 	/*
166 	 * Reset the configuration to its default
167 	 * (This includes command line flags)
168 	 */
169 	memcpy(newconf, &defconf, sizeof *newconf);
170 	newconf->c_refcount = 1;
171 	newconf->c_timestamp = *(time_t *)timestamp;
172 
173 	(void)gettimeofday(&tv1, NULL);
174 
175 	if (!conf_cold || newconf->c_debug)
176 		mg_log(LOG_INFO, "%sloading config file \"%s\"",
177 		    conf_cold ? "" : "re", conffile);
178 
179 	errno = 0;
180 	if ((stream = Fopen(conffile, "r")) == NULL) {
181 		mg_log(LOG_ERR, "cannot open config file %s: %s",
182 		    conffile,
183 		    (errno == 0) ? "out of stdio streams" : strerror(errno));
184 
185 		if (conf_cold)
186 			exit(EX_OSERR);
187 	} else {
188 		SET_CLOEXEC(stream);
189 		TSS_SET(conf_key, newconf);
190 
191 		peer_clear();
192 		ACL_WRLOCK;
193 #ifdef USE_DNSRBL
194 		dnsrbl_clear();
195 #endif
196 #ifdef USE_CURL
197 		urlcheck_clear();
198 #endif
199 #ifdef USE_LDAP
200 		ldapcheck_clear();
201 #endif
202 #ifdef USE_DKIM
203 		dkimcheck_clear();
204 #endif
205 		all_list_clear();
206 		macro_clear();
207 		ratelimit_clear();
208 		acl_clear();
209 
210 		conf_in = stream;
211 		conf_line = 1;
212 		conf_acl_end = 0;
213 		conf_racl_end = 0;
214 		conf_dacl_end = 0;
215 
216 		conf_parse();
217 		conf_dispose_input_file();
218 		ACL_UNLOCK;
219 
220 		TSS_SET(conf_key, threadconf);
221 
222 		Fclose(stream);
223 
224 		if (!conf_cold || newconf->c_debug) {
225 			(void)gettimeofday(&tv2, NULL);
226 			timersub(&tv2, &tv1, &tv3);
227 			mg_log(LOG_INFO,
228 			    "%sloaded config file \"%s\" in %ld.%06lds",
229 			    conf_cold ? "" : "re", conffile,
230 			    tv3.tv_sec, tv3.tv_usec);
231 		}
232 	}
233 
234 	/*
235 	 * Dump the ACL for debugging purposes
236 	 */
237 	if (newconf->c_debug || newconf->c_acldebug)
238 		acl_dump();
239 
240 	CONF_LOCK;
241 	assert(TAILQ_FIRST(&conf_list_head) == currconf);
242 	if (currconf && --currconf->c_refcount == 0) {
243 		TAILQ_REMOVE(&conf_list_head, currconf, c_chain);
244 		free(currconf);
245 	}
246 	TAILQ_INSERT_HEAD(&conf_list_head, newconf, c_chain);
247 	CONF_UNLOCK;
248 
249 #ifdef CONF_DEBUG
250 	conf_dump();
251 #endif
252 	dump_conf_changed();
253 	return NULL;
254 }
255 
256 /* Functions other than main() must not invoke this */
257 void
conf_load(void)258 conf_load(void) {
259 	struct stat st;
260 
261 	if (!conf_specified && strcmp(conffile, legacy_conffile) != 0) {
262 		struct stat st;
263 
264 		if (stat(legacy_conffile, &st) == 0) {
265 			mg_log(LOG_WARNING,
266 			       "legacy configuration file '%s' is used "
267 			       "instead of '%s'.", legacy_conffile, conffile);
268 			conffile = legacy_conffile;
269 		}
270 	}
271 
272 	if (stat(conffile, &st))
273 		st.st_mtime = (time_t)0;
274 
275 	conf_load_internal(&st.st_mtime);
276 }
277 
278 void
conf_update(void)279 conf_update(void) {
280 	struct stat st;
281 	pthread_t tid;
282 	pthread_attr_t attr;
283 	int error;
284 	int need_update;
285 
286 	if (stat(conffile, &st) != 0) {
287 		mg_log(LOG_ERR, "config file \"%s\" unavailable",
288 		    conffile);
289 		return;
290 	}
291 
292 	CONF_LOCK;
293 	while (conf_updating)
294 		pthread_cond_wait(&conf_update_cond, &conf_lock);
295 	conf_updating = need_update =
296 		st.st_mtime > TAILQ_FIRST(&conf_list_head)->c_timestamp;
297 	CONF_UNLOCK;
298 
299 	if (!need_update)
300 		return;
301 
302 	/*
303 	 * On some platforms, the thread stack limit is too low and
304 	 * conf_parse will get a SIGSEGV because it overflows the
305 	 * stack.
306 	 *
307 	 * In order to fix this, we spawn a new thread just for
308 	 * parsing the config file, and we request a stack big
309 	 * enough to hold the parser data. 2 MB seems okay.
310 	 *
311 	 * We do not do that during the initial config load because
312 	 * it is useless and it will trigger a bug on some systems
313 	 * (launching a thread before a fork seems to be a problem)
314 	 */
315 	if ((error = pthread_attr_init(&attr)) != 0) {
316 		mg_log(LOG_ERR, "pthread_attr_init failed: %s",
317 		    strerror(error));
318 		exit(EX_OSERR);
319 	}
320 
321 	if ((error = pthread_attr_setstacksize(&attr,
322 	    2 * 1024 * 1024)) != 0) {
323 		mg_log(LOG_ERR, "pthread_attr_setstacksize failed: %s",
324 		    strerror(error));
325 		exit(EX_OSERR);
326 	}
327 
328 	if ((error = pthread_create(&tid, &attr,
329 	    conf_load_internal, &st.st_mtime)) != 0) {
330 		mg_log(LOG_ERR, "pthread_create failed: %s",
331 		    strerror(error));
332 		exit(EX_OSERR);
333 	}
334 
335 	if ((error = pthread_attr_destroy(&attr)) != 0) {
336 		mg_log(LOG_ERR, "pthread_attr_destroy failed: %s",
337 		    strerror(error));
338 		exit(EX_OSERR);
339 	}
340 
341 	if ((error = pthread_join(tid, NULL)) != 0) {
342 		mg_log(LOG_ERR, "pthread_join failed: %s",
343 		    strerror(error));
344 		exit(EX_OSERR);
345 	}
346 
347 	CONF_LOCK;
348 	conf_updating = 0;
349 	CONF_UNLOCK;
350 	if ((error = pthread_cond_broadcast(&conf_update_cond)) != 0) {
351 		mg_log(LOG_ERR, "pthread_cond_broadcast failed: %s",
352 		       strerror(error));
353 		abort();
354 	}
355 
356 	return;
357 }
358 
359 void
conf_retain(void)360 conf_retain(void) {
361 	struct conf_rec *c;
362 
363 	if (GET_CONF()) {
364 		mg_log(LOG_ERR, "%s:%d BUG: conf_retain called twice?",
365 				__FILE__, __LINE__);
366 		assert(0);
367 	}
368 
369 	CONF_LOCK;
370 	c = TAILQ_FIRST(&conf_list_head);
371 #ifdef CONF_DEBUG
372 	{
373 		char textdate[DATELEN];
374 		struct tm tm;
375 
376 		localtime_r(&c->c_timestamp, &tm);
377 		strftime(textdate, sizeof textdate, "%Y-%m-%d %T",  &tm);
378 		mg_log(LOG_DEBUG, "conf_retain: stamp %s ref %d -> %d",
379 		       textdate, c->c_refcount, c->c_refcount + 1);
380 	}
381 #endif
382 	++c->c_refcount;
383 	CONF_UNLOCK;
384 
385 	TSS_SET(conf_key, c);
386 }
387 
388 void
conf_release(void)389 conf_release(void) {
390 	struct conf_rec *c = GET_CONF();
391 
392 	if (!c) {
393 		mg_log(LOG_ERR, "%s:%d BUG: conf_release before conf_retain",
394 				__FILE__, __LINE__);
395 		assert(0);
396 		return;
397 	}
398 
399 	CONF_LOCK;
400 #ifdef CONF_DEBUG
401 	{
402 		char textdate[DATELEN];
403 		struct tm tm;
404 
405 		localtime_r(&c->c_timestamp, &tm);
406 		strftime(textdate, sizeof textdate, "%Y-%m-%d %T",  &tm);
407 		mg_log(LOG_DEBUG, "conf_release: stamp %s ref %d -> %d",
408 		       textdate, c->c_refcount, c->c_refcount - 1);
409 	}
410 #endif
411 	if (--c->c_refcount == 0) {
412 		TAILQ_REMOVE(&conf_list_head, c, c_chain);
413 		free(c);
414 	}
415 	CONF_UNLOCK;
416 
417 	TSS_SET(conf_key, NULL);
418 }
419 
420 /*
421  * Write path into dst, stripping leading and trailing quotes
422  */
423 char *
quotepath(dst,path,len)424 quotepath(dst, path, len)
425 	char *dst;
426 	char *path;
427 	size_t len;
428 {
429 	path++;	/* strip first quote */
430 	strncpy(dst, path, len);
431 	dst[len] = '\0';
432 
433 	/* Strip trailing quote */
434 	if ((len = strlen(dst)) > 0)
435 		dst[len - 1] = '\0';
436 
437 	return dst;
438 }
439 
440 void
conf_defaults(c)441 conf_defaults(c)
442 	struct conf_rec *c;
443 {
444 	c->c_refcount = -1;
445 	c->c_timestamp = (time_t)0;
446 
447 	c->c_forced = C_GLNONE;
448 	c->c_debug = 0;
449 	c->c_acldebug = 0;
450 	c->c_quiet = 0;
451 	c->c_noauth = 0;
452 	c->c_multiracl = 0;
453 	c->c_noaccessdb = 0;
454 	c->c_nospf = 0;
455 	c->c_delayedreject = 0;
456 	c->c_testmode = 0;
457 	c->c_delay = GLDELAY;
458 	c->c_autowhite_validity = AUTOWHITE_VALIDITY;
459 	c->c_tarpit = TARPIT_DURATION;
460 	c->c_tarpit_scope = TAP_SESSION;
461 	c->c_pidfile = NULL;
462 	c->c_dumpfile = DUMPFILE;
463 	c->c_dumpfile_mode = -1;
464 	prefix2mask4(32, &c->c_match_mask);
465 #ifdef AF_INET6
466 	prefix2mask6(128, &c->c_match_mask6);
467 #endif
468 	c->c_syncaddr = NULL;
469 	c->c_syncport = NULL;
470 	c->c_syncsrcaddr = NULL;
471 	c->c_syncsrcport = NULL;
472 	c->c_socket = NULL;
473 	c->c_socket_mode = -1;
474 	c->c_user = NULL;
475 	c->c_nodetach = 0;
476 	c->c_report = C_ALL;
477 	c->c_dumpfreq = DUMPFREQ;
478 	c->c_timeout = TIMEOUT;
479 	c->c_extendedregex = 0;
480 	c->c_unbracket = 0;
481 	c->c_dracdb = DRACDB;
482 	c->c_nodrac = 0;
483 	c->c_maxpeek = 0;
484 	c->c_logfac = LOG_MAIL;
485 	c->c_domainexact = 0;
486 #ifdef USE_P0F
487 	c->c_p0fsock[0] = '\0';
488 #endif
489 #ifdef USE_SPAMD
490 	c->c_spamdsock[0] = '\0';
491 	c->c_spamdsocktype[0] = '\0';
492 #endif
493 	c->c_syncmaxqlen = SYNC_MAXQLEN;
494 	(void)memset(&c->c_localaddr, 0, sizeof(c->c_localaddr));
495 	(void)memset(&c->c_localaddr_string, 0, sizeof(c->c_localaddr_string));
496 	return;
497 }
498