Skip to content

Commit ae2db5e

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 86d51a7 commit ae2db5e

File tree

12 files changed

+1129
-225
lines changed

12 files changed

+1129
-225
lines changed

doc/doc-docbook/spec.xfpt

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

1588715887

15888+
.new
15889+
.option dnslist_valid_addresses main "host list&!!" &`127.0.0.0/8`&
15890+
.cindex "dnslists ACL condition" "valid addresses"
15891+
This option specifies IP addresses that are acceptable in dnslist responses.
15892+
15893+
Responses containing address records that do not match this list will be logged
15894+
as an error and ignored. This helps identify list domains that have been taken
15895+
over by a domain-parking registrar.
15896+
15897+
The variable &$dnslist_domain$& contains the name of the overall domain that
15898+
matched (for example, &`spamhaus.example`&).
15899+
.wen
15900+
15901+
1588815902
.option drop_cr main boolean false
1588915903
This is an obsolete option that is now a no-op. It used to affect the way Exim
1589015904
handled CR and LF characters in incoming messages. What happens now is
@@ -32601,6 +32615,12 @@ connection (assuming long-enough TTL).
3260132615
Exim does not share information between multiple incoming
3260232616
connections (but your local name server cache should be active).
3260332617

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

src/src/dnsbl.c

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

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

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

@@ -225,6 +297,9 @@ if (cb->rc == DNS_SUCCEED)
225297
const uschar *ptr = iplist;
226298
uschar *res;
227299

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

230305
if (!bitmask)
@@ -249,14 +324,7 @@ if (cb->rc == DNS_SUCCEED)
249324
We change this only for IPv4 addresses in the list. */
250325

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

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

@@ -311,33 +379,6 @@ if (cb->rc == DNS_SUCCEED)
311379
}
312380
}
313381

314-
/* No address list check; discard any illegal returns and give up if
315-
none remain. */
316-
317-
else
318-
{
319-
BOOL ok = FALSE;
320-
for (da = cb->rhs; da; da = da->next)
321-
{
322-
int address[4];
323-
324-
if ( host_aton(da->address, address) == 1 /* ipv4 */
325-
&& (address[0] & 0xff000000) == 0x7f000000 /* 127.0.0.0/8 */
326-
)
327-
ok = TRUE;
328-
else
329-
log_write(0, LOG_MAIN,
330-
"DNS list lookup for %s at %s returned %s;"
331-
" not in 127.0/8 and discarded",
332-
keydomain, domain, da->address);
333-
}
334-
if (!ok)
335-
{
336-
yield = FAIL;
337-
goto out;
338-
}
339-
}
340-
341382
/* Either there was no IP list, or the record matched, implying that the
342383
domain is on the list. We now want to find a corresponding TXT record. If an
343384
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
@@ -898,6 +898,7 @@ int dns_use_edns0 = -1; /* <0 = not coerced */
898898
uschar *dnslist_domain = NULL;
899899
uschar *dnslist_matched = NULL;
900900
uschar *dnslist_text = NULL;
901+
const uschar *dnslist_valid_addresses = US"127.0.0.0/8";
901902
uschar *dnslist_value = NULL;
902903
tree_node *domainlist_anchor = NULL;
903904
int domainlist_count = 0;

src/src/globals.h

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

src/src/readconf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ static optionlist optionlist_config[] = {
143143
{ "dns_retry", opt_int, {&dns_retry} },
144144
{ "dns_trust_aa", opt_stringptr, {&dns_trust_aa} },
145145
{ "dns_use_edns0", opt_int, {&dns_use_edns0} },
146+
{ "dnslist_valid_addresses", opt_stringptr, {&dnslist_valid_addresses} },
146147
/* This option is now a no-op, retained for compatibility */
147148
{ "drop_cr", opt_bool, {&drop_cr} },
148149
/*********************************************************/

src/src/structs.h

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

797797
typedef struct dns_address {
798798
struct dns_address *next;
799+
BOOL dnsbl_invalid;
799800
uschar address[1];
800801
} dns_address;
801802

test/confs/0139

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

12+
.ifdef DNSBL_127_255
13+
# Split into two /9s so that it's visible in the debug output
14+
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}}
15+
.endif
16+
17+
.ifdef DNSBL_DEFER
18+
# Expansion failure
19+
dnslist_valid_addresses = ${if eq{intentional_expansion_failure
20+
.endif
21+
1222
acl_smtp_helo = check_helo
1323
acl_smtp_rcpt = check_recipient
1424
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)