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