Skip to content

Commit 362e85e

Browse files
committed
dnslists: make valid lookup result addresses configurable. Bug 2631
This allows the Spamhaus error addresses to be considered an error, e.g.: dnslist_valid_addresses = ${if match{$dnslist_domain}{\N.spamhaus.org$\N}{!127.255.255.0/24 : }}127.0.0.0/8
1 parent 725900c commit 362e85e

File tree

12 files changed

+1129
-221
lines changed

12 files changed

+1129
-221
lines changed

doc/doc-docbook/spec.xfpt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15814,6 +15814,20 @@ means that DNSSEC will not work with Exim on that platform either, unless Exim
1581415814
is linked against an alternative DNS client library.
1581515815

1581615816

15817+
.new
15818+
.option dnslist_valid_addresses main "host list&!!" &`127.0.0.0/8`&
15819+
.cindex "dnslists ACL condition" "valid addresses"
15820+
This option specifies IP addresses that are acceptable in dnslist responses.
15821+
15822+
Responses containing address records that do not match this list will be logged
15823+
as an error and ignored. This helps identify list domains that have been taken
15824+
over by a domain-parking registrar.
15825+
15826+
The variable &$dnslist_domain$& contains the name of the overall domain that
15827+
matched (for example, &`spamhaus.example`&).
15828+
.wen
15829+
15830+
1581715831
.option drop_cr main boolean false
1581815832
This is an obsolete option that is now a no-op. It used to affect the way Exim
1581915833
handled CR and LF characters in incoming messages. What happens now is
@@ -32368,6 +32382,12 @@ connection (assuming long-enough TTL).
3236832382
Exim does not share information between multiple incoming
3236932383
connections (but your local name server cache should be active).
3237032384

32385+
.new
32386+
DNS list responses are filtered using the &%dnslist_valid_addresses%& host list
32387+
before use to protect against list domains that have been taken over by a
32388+
domain-parking registrar and no longer return values in the 127.0.0.0/8 range.
32389+
.wen
32390+
3237132391
There are a number of DNS lists to choose from, some commercial, some free,
3237232392
or free for small deployments. An overview can be found at
3237332393
&url(https://en.wikipedia.org/wiki/Comparison_of_DNS_blacklists).

src/src/dnsbl.c

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ if (cb->rc == DNS_SUCCEED)
202202
{
203203
dns_address * da = NULL;
204204
uschar *addlist = cb->rhs->address;
205+
uschar *orig_dnslist_domain = NULL;
206+
int filter_rc = FAIL;
205207

206208
/* For A and AAAA records, there may be multiple addresses from multiple
207209
records. For A6 records (currently not expected to be used) there may be
@@ -213,6 +215,76 @@ if (cb->rc == DNS_SUCCEED)
213215
HDEBUG(D_dnsbl) debug_printf("DNS lookup for %s succeeded (yielding %s)\n",
214216
query, addlist);
215217

218+
/* Make dnslist_domain available to dnslist_valid_addresses expansion. */
219+
orig_dnslist_domain = dnslist_domain;
220+
dnslist_domain = domain_txt;
221+
222+
for (da = cb->rhs; da; da = da->next)
223+
{
224+
switch (verify_check_this_host(&dnslist_valid_addresses, NULL, US"", da->address, NULL))
225+
{
226+
case OK:
227+
da->dnsbl_invalid = FALSE;
228+
229+
if (filter_rc != DEFER)
230+
filter_rc = OK;
231+
break;
232+
233+
case FAIL:
234+
da->dnsbl_invalid = TRUE;
235+
addlist = NULL;
236+
237+
log_write(0, LOG_MAIN,
238+
"DNS list lookup for %s at %s returned %s;"
239+
" invalid address discarded",
240+
keydomain, domain, da->address);
241+
break;
242+
243+
case DEFER:
244+
log_write(0, LOG_MAIN,
245+
"DNS list lookup for %s at %s returned %s;"
246+
" unable to verify, returned DEFER",
247+
keydomain, domain, da->address);
248+
249+
filter_rc = DEFER;
250+
break;
251+
}
252+
}
253+
254+
dnslist_domain = orig_dnslist_domain;
255+
256+
if (filter_rc == FAIL)
257+
{
258+
HDEBUG(D_dnsbl)
259+
{
260+
debug_printf("=> all addresses are invalid\n");
261+
debug_printf("=> that means %s is not listed at %s\n",
262+
keydomain, domain);
263+
}
264+
}
265+
266+
if (filter_rc != OK) return filter_rc;
267+
268+
/* Need to recreate addlist without filtered addresses. */
269+
if (addlist == NULL)
270+
{
271+
for (da = cb->rhs; da; da = da->next)
272+
{
273+
if (da->dnsbl_invalid)
274+
continue;
275+
276+
if (addlist == NULL)
277+
addlist = da->address;
278+
else
279+
addlist = string_sprintf("%s, %s", addlist, da->address);
280+
}
281+
282+
HDEBUG(D_dnsbl)
283+
{
284+
debug_printf("=> updated address list: %s\n", addlist);
285+
}
286+
}
287+
216288
/* Address list check; this can be either for equality, or via a bitmask.
217289
In the latter case, all the bits must match. */
218290

@@ -224,6 +296,9 @@ if (cb->rc == DNS_SUCCEED)
224296
const uschar *ptr = iplist;
225297
uschar *res;
226298

299+
if (da->dnsbl_invalid)
300+
continue;
301+
227302
/* Handle exact matching */
228303

229304
if (!bitmask)
@@ -248,14 +323,7 @@ if (cb->rc == DNS_SUCCEED)
248323
We change this only for IPv4 addresses in the list. */
249324

250325
if (host_aton(da->address, address) == 1)
251-
if ((address[0] & 0xff000000) != 0x7f000000) /* 127.0.0.0/8 */
252-
log_write(0, LOG_MAIN,
253-
"DNS list lookup for %s at %s returned %s;"
254-
" not in 127.0/8 and discarded",
255-
keydomain, domain, da->address);
256-
257-
else
258-
mask = address[0];
326+
mask = address[0];
259327

260328
/* Scan the returned addresses, skipping any that are IPv6 */
261329

@@ -309,29 +377,6 @@ if (cb->rc == DNS_SUCCEED)
309377
}
310378
}
311379

312-
/* No address list check; discard any illegal returns and give up if
313-
none remain. */
314-
315-
else
316-
{
317-
BOOL ok = FALSE;
318-
for (da = cb->rhs; da; da = da->next)
319-
{
320-
int address[4];
321-
322-
if ( host_aton(da->address, address) == 1 /* ipv4 */
323-
&& (address[0] & 0xff000000) == 0x7f000000 /* 127.0.0.0/8 */
324-
)
325-
ok = TRUE;
326-
else
327-
log_write(0, LOG_MAIN,
328-
"DNS list lookup for %s at %s returned %s;"
329-
" not in 127.0/8 and discarded",
330-
keydomain, domain, da->address);
331-
}
332-
if (!ok) return FAIL;
333-
}
334-
335380
/* Either there was no IP list, or the record matched, implying that the
336381
domain is on the list. We now want to find a corresponding TXT record. If an
337382
alternate domain is specified for the TXT record, call this function

src/src/globals.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,7 @@ int dns_use_edns0 = -1; /* <0 = not coerced */
886886
uschar *dnslist_domain = NULL;
887887
uschar *dnslist_matched = NULL;
888888
uschar *dnslist_text = NULL;
889+
const uschar *dnslist_valid_addresses = US"127.0.0.0/8";
889890
uschar *dnslist_value = NULL;
890891
tree_node *domainlist_anchor = NULL;
891892
int domainlist_count = 0;

src/src/globals.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,7 @@ extern int dns_use_edns0; /* Coerce EDNS0 support on/off in resolve
554554
extern uschar *dnslist_domain; /* DNS (black) list domain */
555555
extern uschar *dnslist_matched; /* DNS (black) list matched key */
556556
extern uschar *dnslist_text; /* DNS (black) list text message */
557+
extern const uschar *dnslist_valid_addresses; /* DNS list IP addresses that are considered valid (127.0.0.0/8) */
557558
extern uschar *dnslist_value; /* DNS (black) list IP address */
558559
extern tree_node *domainlist_anchor; /* Tree of defined domain lists */
559560
extern int domainlist_count; /* Number defined */

src/src/readconf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ static optionlist optionlist_config[] = {
140140
{ "dns_retry", opt_int, {&dns_retry} },
141141
{ "dns_trust_aa", opt_stringptr, {&dns_trust_aa} },
142142
{ "dns_use_edns0", opt_int, {&dns_use_edns0} },
143+
{ "dnslist_valid_addresses", opt_stringptr, {&dnslist_valid_addresses} },
143144
/* This option is now a no-op, retained for compatibility */
144145
{ "drop_cr", opt_bool, {&drop_cr} },
145146
/*********************************************************/

src/src/structs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ address. */
794794

795795
typedef struct dns_address {
796796
struct dns_address *next;
797+
BOOL dnsbl_invalid;
797798
uschar address[1];
798799
} dns_address;
799800

test/confs/0139

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
domainlist local_domains = exim.test.ex
99
trusted_users = CALLER
1010

11+
.ifdef DNSBL_127_255
12+
# Split into two /9s so that it's visible in the debug output
13+
dnslist_valid_addresses = ${if eq{$dnslist_domain}{rbl.test.ex}{!127.255.255.0/24 : 127.0.0.0/9 : 127.128.0.0/9}{127.0.0.0/8}}
14+
.endif
15+
16+
.ifdef DNSBL_DEFER
17+
# Expansion failure
18+
dnslist_valid_addresses = ${if eq{intentional_expansion_failure
19+
.endif
20+
1121
acl_smtp_helo = check_helo
1222
acl_smtp_rcpt = check_recipient
1323
acl_smtp_mail = check_mail

test/dnszones-src/db.test.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,15 @@ TTL=2 14.12.11.V4NET.rbl A 127.0.0.2
212212
105.13.13.V4NET.rbl A 255.255.255.255
213213
A 255.255.255.254
214214
215+
; Configuration to consider 127.255.255.0/24 as invalid
216+
217+
106.13.13.V4NET.rbl A 127.255.255.255
218+
219+
; Exact match along with invalid return value
220+
221+
107.13.13.V4NET.rbl A 127.0.0.1
222+
107.13.13.V4NET.rbl A 128.0.0.0
223+
215224
; -------- Testing MX records --------
216225
217226
mxcased MX 5 ten-99.TEST.EX.

test/log/0509

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
1999-03-02 09:44:33 rbl.test.ex/<;1.2.3.4;V4NET.11.12.13
2-
1999-03-02 09:44:33 DNS list lookup for ten-1 at test.ex returned V4NET.0.0.1; not in 127.0/8 and discarded
2+
1999-03-02 09:44:33 DNS list lookup for ten-1 at test.ex returned V4NET.0.0.1; invalid address discarded
33
1999-03-02 09:44:33 test.ex/a.b.c.d::ten-1::localhost
44
1999-03-02 09:44:33 U=CALLER rejected connection in "connect" ACL

test/scripts/0000-Basic/0139

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,24 @@ exim -bh V4NET.13.13.105
6565
vrfy a@b
6666
quit
6767
****
68+
exim -bh V4NET.13.13.106
69+
vrfy a@b
70+
quit
71+
****
72+
exim -DDNSBL_127_255 -bh V4NET.13.13.2
73+
vrfy a@b
74+
quit
75+
****
76+
exim -DDNSBL_127_255 -bh V4NET.13.13.106
77+
vrfy a@b
78+
quit
79+
****
80+
exim -DDNSBL_DEFER -bh V4NET.13.13.2
81+
vrfy a@b
82+
quit
83+
****
84+
exim -bh V4NET.13.13.107
85+
vrfy a@b
86+
quit
87+
****
6888
no_msglog_check

0 commit comments

Comments
 (0)