1 /*
2 * Copyright (c) 2007-2012, Vsevolod Stakhov
3 * All rights reserved.
4
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer. Redistributions in binary form
9 * must reproduce the above copyright notice, this list of conditions and the
10 * following disclaimer in the documentation and/or other materials provided with
11 * the distribution. Neither the name of the author nor the names of its
12 * contributors may be used to endorse or promote products derived from this
13 * software without specific prior written permission.
14
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <assert.h>
28 #include "config.h"
29
30 #include "cfg_file.h"
31 #include "rmilter.h"
32
33 extern int yylineno;
34 extern char *yytext;
35 extern char* fnames_stack[];
36 extern int include_stack_ptr;
37
parse_err(const char * fmt,...)38 void parse_err(const char *fmt, ...)
39 {
40 va_list aq;
41 char logbuf[BUFSIZ], readbuf[32];
42 int r;
43
44 va_start (aq, fmt);
45 rmilter_strlcpy (readbuf, yytext, sizeof(readbuf));
46
47 if (include_stack_ptr > 0) {
48 r = snprintf(logbuf, sizeof(logbuf), "config file <%s> parse error! line: %d, "
49 "text: %s, reason: ", fnames_stack[include_stack_ptr - 1],
50 yylineno, readbuf);
51 }
52 else {
53 r = snprintf(logbuf, sizeof(logbuf), "config file parse error! line: %d, "
54 "text: %s, reason: ",
55 yylineno, readbuf);
56 }
57 r += vsnprintf(logbuf + r, sizeof(logbuf) - r, fmt, aq);
58
59 va_end (aq);
60 fprintf (stderr, "%s\n", logbuf);
61 syslog (LOG_ERR, "%s", logbuf);
62 }
63
parse_warn(const char * fmt,...)64 void parse_warn(const char *fmt, ...)
65 {
66 va_list aq;
67 char logbuf[BUFSIZ], readbuf[32];
68 int r;
69
70 va_start (aq, fmt);
71 rmilter_strlcpy (readbuf, yytext, sizeof(readbuf));
72
73 r = snprintf(logbuf, sizeof(logbuf),
74 "config file parse warning! line: %d, text: %s, reason: ", yylineno,
75 readbuf);
76 r += vsnprintf(logbuf + r, sizeof(logbuf) - r, fmt, aq);
77
78 va_end (aq);
79 syslog (LOG_ERR, "%s", logbuf);
80 }
81
copy_regexp(char ** dst,const char * src)82 static size_t copy_regexp(char **dst, const char *src)
83 {
84 size_t len;
85 if (!src || *src == '\0')
86 return 0;
87
88 len = strlen (src);
89
90 /* Skip slashes */
91 if (*src == '/') {
92 src++;
93 len--;
94 }
95 if (src[len - 1] == '/') {
96 len--;
97 }
98
99 *dst = malloc (len + 1);
100 if (!*dst)
101 return 0;
102
103 return rmilter_strlcpy (*dst, src, len + 1);
104 }
105
add_cache_server(struct config_file * cf,char * str,char * str2,int type)106 int add_cache_server(struct config_file *cf, char *str, char *str2,
107 int type)
108 {
109 char *cur_tok, *err_str;
110 struct cache_server *mc = NULL;
111 uint16_t port;
112 unsigned *pnum;
113
114 if (str == NULL) {
115 return 0;
116 }
117
118 switch (type) {
119 case CACHE_SERVER_GREY:
120 pnum = &cf->cache_servers_grey_num;
121 mc = cf->cache_servers_grey;
122 break;
123 case CACHE_SERVER_WHITE:
124 pnum = &cf->cache_servers_white_num;
125 mc = cf->cache_servers_white;
126 break;
127 case CACHE_SERVER_LIMITS:
128 pnum = &cf->cache_servers_limits_num;
129 mc = cf->cache_servers_limits;
130 break;
131 case CACHE_SERVER_ID:
132 pnum = &cf->cache_servers_id_num;
133 mc = cf->cache_servers_id;
134 break;
135 case CACHE_SERVER_COPY:
136 pnum = &cf->cache_servers_copy_num;
137 mc = cf->cache_servers_copy;
138 break;
139 case CACHE_SERVER_SPAM:
140 pnum = &cf->cache_servers_spam_num;
141 mc = cf->cache_servers_spam;
142 break;
143 }
144
145 if (*pnum >= MAX_CACHE_SERVERS) {
146 yyerror ("yyparse: maximum number of cache servers is reached %d",
147 MAX_CACHE_SERVERS);
148 return 0;
149 }
150
151 mc += *pnum;
152 cur_tok = strsep (&str, ":");
153
154 if (cur_tok == NULL || *cur_tok == '\0') {
155 return 0;
156 }
157
158 /* cur_tok - server name, str - server port */
159 if (str == NULL) {
160 port = DEFAULT_MEMCACHED_PORT;
161 }
162 else {
163 port = strtoul (str, &err_str, 10);
164 if (*err_str != '\0') {
165 yyerror ("yyparse: bad memcached port: %s", str);
166 return 0;
167 }
168 }
169
170 mc->addr = strdup (cur_tok);
171 mc->port = port;
172
173 if (str2 != NULL) {
174 msg_warn("mirrored servers are no longer supported; "
175 "server %s will be ignored", str2);
176 }
177
178 (*pnum)++;
179
180 return 1;
181 }
182
add_clamav_server(struct config_file * cf,char * str)183 int add_clamav_server(struct config_file *cf, char *str)
184 {
185 char *cur_tok, *err_str;
186 struct clamav_server *srv;
187 struct hostent *he;
188
189 if (str == NULL)
190 return 0;
191
192 if (cf->clamav_servers_num == MAX_CLAMAV_SERVERS) {
193 yyerror ("yyparse: maximum number of clamav servers is reached %d",
194 MAX_CLAMAV_SERVERS);
195 }
196
197 srv = &cf->clamav_servers[cf->clamav_servers_num];
198
199 if (srv == NULL)
200 return 0;
201
202 cur_tok = strsep (&str, ":");
203
204 if (cur_tok == NULL || *cur_tok == '\0') {
205 return 0;
206 }
207
208 srv->name = strdup (cur_tok);
209
210 if (str != NULL) {
211 /* We have also port */
212 srv->port = strtoul (str, NULL, 10);
213 }
214
215 /* Try to parse priority */
216 cur_tok = strsep (&str, ":");
217 if (str != NULL && *str != '\0') {
218 srv->up.priority = strtoul (str, NULL, 10);
219 }
220
221 cf->clamav_servers_num ++;
222
223 return 1;
224 }
225
add_spamd_server(struct config_file * cf,char * str,int is_extra)226 int add_spamd_server(struct config_file *cf, char *str, int is_extra)
227 {
228 char *cur_tok, *err_str;
229 struct spamd_server *srv;
230 struct hostent *he;
231
232 if (str == NULL)
233 return 0;
234
235 if (is_extra) {
236 if (cf->extra_spamd_servers_num == MAX_SPAMD_SERVERS) {
237 yyerror ("yyparse: maximum number of spamd servers is reached %d",
238 MAX_SPAMD_SERVERS);
239 return -1;
240 }
241 }
242 else {
243 if (cf->spamd_servers_num == MAX_SPAMD_SERVERS) {
244 yyerror ("yyparse: maximum number of spamd servers is reached %d",
245 MAX_SPAMD_SERVERS);
246 return -1;
247 }
248 }
249
250 if (is_extra) {
251 srv = &cf->extra_spamd_servers[cf->extra_spamd_servers_num];
252 }
253 else {
254 srv = &cf->spamd_servers[cf->spamd_servers_num];
255 }
256
257 if (*str == 'r' && *(str + 1) == ':') {
258 srv->type = SPAMD_RSPAMD;
259 str += 2;
260 }
261 else {
262 srv->type = SPAMD_RSPAMD;
263 }
264
265 cur_tok = strsep (&str, ":");
266
267 if (cur_tok == NULL || *cur_tok == '\0') {
268 return 0;
269 }
270
271 srv->name = strdup (cur_tok);
272
273 if (str != NULL) {
274 /* We have also port */
275 srv->port = strtoul (str, NULL, 10);
276 }
277
278 /* Try to parse priority */
279 cur_tok = strsep (&str, ":");
280 if (str != NULL && *str != '\0') {
281 srv->up.priority = strtoul (str, NULL, 10);
282 }
283
284 if (is_extra) {
285 cf->extra_spamd_servers_num++;
286 }
287 else {
288 cf->spamd_servers_num++;
289 }
290 return 1;
291 }
292
add_ip_radix(radix_compressed_t ** tree,char * ipnet)293 int add_ip_radix (radix_compressed_t **tree, char *ipnet)
294 {
295 if (!radix_add_generic_iplist (ipnet, tree, true)) {
296 yyerror ("add_ip_radix: cannot insert ip to tree: %s",
297 ipnet);
298 return 0;
299 }
300
301 return 1;
302 }
303
304 #ifdef WITH_DKIM
add_hashed_header(const char * name,struct dkim_hash_entry ** hash)305 static void add_hashed_header(const char *name, struct dkim_hash_entry **hash)
306 {
307 struct dkim_hash_entry *new;
308
309 new = malloc (sizeof(struct dkim_hash_entry));
310 new->name = strdup (name);
311 rmilter_str_lc (new->name, strlen (new->name));
312 HASH_ADD_KEYPTR (hh, *hash, new->name, strlen (new->name), new);
313 }
314 #endif
315
init_defaults(struct config_file * cfg)316 void init_defaults(struct config_file *cfg)
317 {
318 memset (cfg, 0, sizeof (*cfg));
319
320 cfg->wlist_rcpt_global = NULL;
321 cfg->wlist_rcpt_limit = NULL;
322 cfg->clamav_connect_timeout = DEFAULT_CLAMAV_CONNECT_TIMEOUT;
323 cfg->clamav_port_timeout = DEFAULT_CLAMAV_PORT_TIMEOUT;
324 cfg->clamav_results_timeout = DEFAULT_CLAMAV_RESULTS_TIMEOUT;
325 cfg->cache_connect_timeout = DEFAULT_MEMCACHED_CONNECT_TIMEOUT;
326 cfg->spamd_connect_timeout = DEFAULT_SPAMD_CONNECT_TIMEOUT;
327 cfg->spamd_results_timeout = DEFAULT_SPAMD_RESULTS_TIMEOUT;
328
329 cfg->clamav_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
330 cfg->clamav_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
331 cfg->clamav_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
332
333 cfg->spamd_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
334 cfg->spamd_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
335 cfg->spamd_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
336 cfg->spamd_reject_message = strdup (DEFAUL_SPAMD_REJECT);
337 cfg->rspamd_metric = strdup (DEFAULT_RSPAMD_METRIC);
338 cfg->spam_header = strdup (DEFAULT_SPAM_HEADER);
339 cfg->spam_header_value = strdup (DEFAULT_SPAM_HEADER_VALUE);
340 cfg->spamd_retry_count = DEFAULT_SPAMD_RETRY_COUNT;
341 cfg->spamd_retry_timeout = DEFAULT_SPAMD_RETRY_TIMEOUT;
342 cfg->spamd_temp_fail = 0;
343 cfg->spam_bar_char = strdup ("x");
344
345 cfg->cache_error_time = DEFAULT_UPSTREAM_ERROR_TIME;
346 cfg->cache_dead_time = DEFAULT_UPSTREAM_DEAD_TIME;
347 cfg->cache_maxerrors = DEFAULT_UPSTREAM_MAXERRORS;
348
349 cfg->grey_whitelist_tree = radix_create_compressed ();
350 cfg->limit_whitelist_tree = radix_create_compressed ();
351 cfg->spamd_whitelist = radix_create_compressed ();
352 cfg->clamav_whitelist = radix_create_compressed ();
353 cfg->dkim_ip_tree = radix_create_compressed ();
354 cfg->our_networks = radix_create_compressed ();
355 cfg->greylisted_message = strdup (DEFAULT_GREYLISTED_MESSAGE);
356 /* Defaults for greylisting */
357 /* 1d for greylisting data */
358 cfg->greylisting_expire = 86400;
359 /* 3d for whitelisting */
360 cfg->whitelisting_expire = cfg->greylisting_expire * 3;
361 cfg->greylisting_timeout = 300;
362 cfg->white_prefix = strdup ("white");
363 cfg->grey_prefix = strdup ("grey");
364 cfg->id_prefix = strdup ("id");
365 cfg->spamd_spam_add_header = 1;
366
367 cfg->cache_copy_prob = 100.0;
368
369 cfg->spamd_soft_fail = 1;
370 cfg->spamd_greylist = 1;
371 cfg->greylisting_enable = 1;
372 cfg->ratelimit_enable = 1;
373
374 cfg->dkim_auth_only = 1;
375 cfg->dkim_enable = 1;
376 cfg->pid_file = NULL;
377 cfg->tempfiles_mode = 00600;
378
379 #if 0
380 /* Init static defaults */
381 white_from_abuse.addr = "abuse";
382 white_from_abuse.len = sizeof ("abuse") - 1;
383 white_from_postmaster.addr = "postmaster";
384 white_from_postmaster.len = sizeof ("postmaster") - 1;
385 LIST_INSERT_HEAD (&cfg->whitelist_static, &white_from_abuse, next);
386 LIST_INSERT_HEAD (&cfg->whitelist_static, &white_from_postmaster, next);
387 #endif
388
389 #ifdef WITH_DKIM
390 cfg->dkim_lib = dkim_init (NULL, NULL);
391 /* Add recommended by rfc headers */
392 add_hashed_header ("from", &cfg->headers);
393 add_hashed_header ("sender", &cfg->headers);
394 add_hashed_header ("reply-to", &cfg->headers);
395 add_hashed_header ("subject", &cfg->headers);
396 add_hashed_header ("date", &cfg->headers);
397 add_hashed_header ("message-id", &cfg->headers);
398 add_hashed_header ("to", &cfg->headers);
399 add_hashed_header ("cc", &cfg->headers);
400 add_hashed_header ("date", &cfg->headers);
401 add_hashed_header ("mime-version", &cfg->headers);
402 add_hashed_header ("content-type", &cfg->headers);
403 add_hashed_header ("content-transfer-encoding", &cfg->headers);
404 add_hashed_header ("resent-to", &cfg->headers);
405 add_hashed_header ("resent-cc", &cfg->headers);
406 add_hashed_header ("resent-from", &cfg->headers);
407 add_hashed_header ("resent-sender", &cfg->headers);
408 add_hashed_header ("resent-message-id", &cfg->headers);
409 add_hashed_header ("in-reply-to", &cfg->headers);
410 add_hashed_header ("references", &cfg->headers);
411 add_hashed_header ("list-id", &cfg->headers);
412 add_hashed_header ("list-owner", &cfg->headers);
413 add_hashed_header ("list-unsubscribe", &cfg->headers);
414 add_hashed_header ("list-subscribe", &cfg->headers);
415 add_hashed_header ("list-post", &cfg->headers);
416 /* TODO: make it configurable */
417 #endif
418 }
419
free_config(struct config_file * cfg)420 void free_config(struct config_file *cfg)
421 {
422 unsigned int i;
423 struct addr_list_entry *addr_cur, *addr_tmp;
424
425 if (cfg->pid_file) {
426 free (cfg->pid_file);
427 }
428 if (cfg->temp_dir) {
429 free (cfg->temp_dir);
430 }
431 if (cfg->sock_cred) {
432 free (cfg->sock_cred);
433 }
434
435 if (cfg->special_mid_re) {
436 pcre_free (cfg->special_mid_re);
437 }
438
439 for (i = 0; i < cfg->clamav_servers_num; i++) {
440 free (cfg->clamav_servers[i].name);
441 }
442 for (i = 0; i < cfg->spamd_servers_num; i++) {
443 free (cfg->spamd_servers[i].name);
444 }
445
446 /* Free whitelists and bounce list*/
447 clear_rcpt_whitelist (&cfg->wlist_rcpt_global);
448 clear_rcpt_whitelist (&cfg->wlist_rcpt_limit);
449 clear_rcpt_whitelist (&cfg->extended_rcpts);
450
451 HASH_ITER (hh, cfg->bounce_addrs, addr_cur, addr_tmp) {
452 HASH_DEL (cfg->bounce_addrs, addr_cur);
453 free (addr_cur->addr);
454 free (addr_cur);
455 }
456
457 radix_destroy_compressed (cfg->grey_whitelist_tree);
458 radix_destroy_compressed (cfg->spamd_whitelist);
459 radix_destroy_compressed (cfg->clamav_whitelist);
460 radix_destroy_compressed (cfg->limit_whitelist_tree);
461 radix_destroy_compressed (cfg->dkim_ip_tree);
462 radix_destroy_compressed (cfg->our_networks);
463
464 if (cfg->spamd_reject_message) {
465 free (cfg->spamd_reject_message);
466 }
467 if (cfg->rspamd_metric) {
468 free (cfg->rspamd_metric);
469 }
470 if (cfg->spam_header) {
471 free (cfg->spam_header);
472 }
473 if (cfg->spam_header_value) {
474 free (cfg->spam_header_value);
475 }
476 if (cfg->id_prefix) {
477 free (cfg->id_prefix);
478 }
479 if (cfg->grey_prefix) {
480 free (cfg->grey_prefix);
481 }
482 if (cfg->white_prefix) {
483 free (cfg->white_prefix);
484 }
485 if (cfg->cache_password) {
486 free (cfg->cache_password);
487 }
488 if (cfg->cache_dbname) {
489 free (cfg->cache_dbname);
490 }
491 if (cfg->cache_copy_channel) {
492 free (cfg->cache_copy_channel);
493 }
494 if (cfg->cache_spam_channel) {
495 free (cfg->cache_spam_channel);
496 }
497 if (cfg->greylisted_message) {
498 free (cfg->greylisted_message);
499 }
500 if (cfg->spam_bar_char) {
501 free (cfg->spam_bar_char);
502 }
503
504 #ifdef WITH_DKIM
505 struct dkim_hash_entry *curh, *tmph;
506 struct dkim_domain_entry *curd, *tmpd;
507
508 if (cfg->dkim_lib) {
509 dkim_close (cfg->dkim_lib);
510 }
511 HASH_ITER (hh, cfg->headers, curh, tmph) {
512 HASH_DEL (cfg->headers, curh); /* delete; users advances to next */
513 free (curh->name);
514 free (curh);
515 }
516 HASH_ITER (hh, cfg->dkim_domains, curd, tmpd) {
517 HASH_DEL (cfg->dkim_domains, curd); /* delete; users advances to next */
518 if (curd->key != MAP_FAILED && curd->key != NULL) {
519 munmap (curd->key, curd->keylen);
520 }
521 if (curd->domain) {
522 free (curd->domain);
523 }
524 if (curd->selector) {
525 free (curd->selector);
526 }
527 if (curd->keyfile) {
528 free (curd->keyfile);
529 }
530 free (curd);
531 }
532 #endif
533 }
534 void
add_rcpt_whitelist(struct whitelisted_rcpt_entry ** head,const char * rcpt)535 add_rcpt_whitelist (struct whitelisted_rcpt_entry **head, const char *rcpt)
536 {
537 struct whitelisted_rcpt_entry *t;
538 t = (struct whitelisted_rcpt_entry *) malloc (
539 sizeof(struct whitelisted_rcpt_entry));
540 if (*rcpt == '@') {
541 t->type = WLIST_RCPT_DOMAIN;
542 rcpt++;
543 }
544 else if (strchr (rcpt, '@') != NULL) {
545 t->type = WLIST_RCPT_USERDOMAIN;
546 }
547 else {
548 t->type = WLIST_RCPT_USER;
549 }
550 t->rcpt = strdup (rcpt);
551 t->len = strlen (t->rcpt);
552
553 HASH_ADD_KEYPTR (hh, *head, t->rcpt, t->len, t);
554 }
555
556 void
clear_rcpt_whitelist(struct whitelisted_rcpt_entry ** head)557 clear_rcpt_whitelist (struct whitelisted_rcpt_entry **head)
558 {
559 struct whitelisted_rcpt_entry *t, *tmp;
560
561 HASH_ITER (hh, *head, t, tmp) {
562 HASH_DEL (*head, t);
563 free (t->rcpt);
564 free (t);
565 }
566 }
567
568 int
is_whitelisted_rcpt(struct whitelisted_rcpt_entry ** head,const char * str)569 is_whitelisted_rcpt (struct whitelisted_rcpt_entry **head, const char *str)
570 {
571 int len;
572 struct whitelisted_rcpt_entry *entry, *list;
573 char rcptbuf[ADDRLEN + 1], *domain;
574
575 if (*str == '<') {
576 str++;
577 }
578
579 len = strcspn (str, ">");
580 rmilter_strlcpy (rcptbuf, str, MIN(len + 1, sizeof(rcptbuf)));
581 rmilter_str_lc (rcptbuf, strlen (rcptbuf));
582
583 if (len > 0) {
584 list = *head;
585 /* Initially search for userdomain */
586 HASH_FIND_STR(list, rcptbuf, entry);
587 if (entry != NULL && entry->type == WLIST_RCPT_USERDOMAIN) {
588 return 1;
589 }
590
591 domain = strchr (rcptbuf, '@');
592 if (domain == NULL && entry != NULL && entry->type == WLIST_RCPT_USER) {
593 return 1;
594 }
595
596 /* Search for user */
597 if (domain != NULL) {
598 *domain = '\0';
599 }
600
601 HASH_FIND_STR (list, rcptbuf, entry);
602 if (entry != NULL && entry->type == WLIST_RCPT_USER) {
603 return 1;
604 }
605 if (domain != NULL) {
606 /* Search for domain */
607 domain++;
608 HASH_FIND_STR (list, domain, entry);
609 if (entry != NULL && entry->type == WLIST_RCPT_DOMAIN) {
610 return 1;
611 }
612 }
613 }
614
615 return 0;
616 }
617
618 char *
trim_quotes(char * in)619 trim_quotes(char *in)
620 {
621 char *res = in;
622 size_t len;
623
624 assert(in != NULL);
625
626 len = strlen (in);
627
628 if (*in == '"') {
629 res = strdup (in + 1);
630 len = strlen (res);
631 free (in);
632 }
633
634 if (len > 1 && res[len - 1] == '"') {
635 res[len - 1] = '\0';
636 }
637
638 return res;
639 }
640
641 /*
642 * vi:ts=4
643 */
644