From d6870e76cf0b838eab1929e5d5afb486c4e7b448 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sat, 6 Feb 2021 21:46:15 +0000 Subject: [PATCH] Fix handling of server which follows a RCPT 452 with a 250. Bug 26092 --- doc/doc-txt/ChangeLog | 5 +++ src/src/transports/smtp.c | 17 ++++---- test/confs/0622 | 79 ++++++++++++++++++++++++++++++++++++ test/log/0622 | 31 ++++++++++++++ test/scripts/0000-Basic/0622 | 17 ++++++++ test/stderr/0143 | 2 +- test/stderr/0398 | 2 +- test/stderr/0432 | 2 +- test/stderr/0476 | 2 +- test/stderr/2035 | 2 +- test/stderr/2135 | 2 +- test/stderr/4052 | 2 +- test/stderr/5410 | 6 +-- test/stderr/5420 | 6 +-- 14 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 test/confs/0622 create mode 100644 test/log/0622 create mode 100644 test/scripts/0000-Basic/0622 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 754ae7b49..e4c1e9a2b 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -198,6 +198,11 @@ JH/41 Fix daemon SIGHUP on FreeBSD. Previously, a named socket for IPC was This affected any platform not supporting "abstract" Unix-domain sockets (i.e. not Linux). +JH/42 Bug 2692: Harden against a peer which reneges on a 452 "too many + recipients" response to RCPT in a later response, with a 250. The + previous coding assumed this would not happen, and under PIPELINING + would result in both lost and duplicate recipients for a message. + diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 30669d351..33051a5e2 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1036,7 +1036,7 @@ if (sx->pending_MAIL) { DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__); count--; - sx->pending_MAIL = FALSE; + sx->pending_MAIL = sx->RCPT_452 = FALSE; if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) { @@ -1082,7 +1082,7 @@ while (count-- > 0) /* The address was accepted */ addr->host_used = sx->conn_args.host; - DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__); + DEBUG(D_transport) debug_printf("%s expect rcpt for %s\n", __FUNCTION__, addr->address); if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) { @@ -1176,7 +1176,7 @@ while (count-- > 0) if (addr->more_errno >> 8 == 52 && yield & 3) { - if (!sx->RCPT_452) + if (!sx->RCPT_452) /* initialised at MAIL-ack above */ { DEBUG(D_transport) debug_printf("%s: seen first 452 too-many-rcpts\n", __FUNCTION__); @@ -1223,6 +1223,8 @@ while (count-- > 0) } } } + if (count && !(addr = addr->next)) + return -2; } /* Loop for next RCPT response */ /* Update where to start at for the next block of responses, unless we @@ -3883,15 +3885,16 @@ else !sx->lmtp ) { - const uschar *s = string_printing(sx->buffer); + const uschar * s = string_printing(sx->buffer); /* deconst cast ok here as string_printing was checked to have alloc'n'copied */ - conf = (s == sx->buffer)? US string_copy(s) : US s; + conf = s == sx->buffer ? US string_copy(s) : US s; } /* Process all transported addresses - for LMTP or PRDR, read a status for - each one. */ + each one. We used to drop out at first_addr, until someone returned a 452 + followed by a 250... and we screwed up the accepted addresses. */ - for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) { if (addr->transport_return != PENDING_OK) continue; diff --git a/test/confs/0622 b/test/confs/0622 new file mode 100644 index 000000000..5dc3e0f94 --- /dev/null +++ b/test/confs/0622 @@ -0,0 +1,79 @@ +# Exim test configuration 0622 + +SERVER= + +.include DIR/aux-var/std_conf_prefix + +primary_hostname = myhost.test.ex +log_selector = +pipelining +received_recipients + + +# ----- Main settings ----- + +acl_smtp_connect = check_connect +acl_smtp_rcpt = check_rcpt + +begin acl + +check_connect: + accept + logwrite = :main:New connection + +check_rcpt: + defer + condition = ${if eq {$acl_m0}{1}} + recipients = B@test.ex : C@test.ex : D@test.ex : E@test.ex : F@test.ex + message = 452 4.5.3 Try again + + defer + condition = ${if eq {$acl_m0}{2}} + recipients = C@test.ex : D@test.ex + message = 452 4.5.3 Try again + + warn + recipients = A@test.ex + set acl_m0 = 1 + + warn + recipients = B@test.ex + set acl_m0 = 2 + + accept + + +# ----- Routers ----- + +begin routers + +server: + driver = redirect + condition = ${if eq {SERVER}{server} {yes}{no}} + data = :blackhole: + +client: + driver = accept + condition = ${if eq {SERVER}{server} {no}{yes}} + retry_use_local_part + transport = send_to_server + + +# ----- Transports ----- + +begin transports + +send_to_server: + driver = smtp + allow_localhost + hosts = 127.0.0.1 + port = PORT_D + max_rcpt = 0 + +# ----- Retry ----- + + +begin retry + +* * F,5d,10s + + +# End diff --git a/test/log/0622 b/test/log/0622 new file mode 100644 index 000000000..7f8edd25f --- /dev/null +++ b/test/log/0622 @@ -0,0 +1,31 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss for A@test.ex B@test.ex C@test.ex D@test.ex E@test.ex F@test.ex +1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaY-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 => b@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaZ-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 => c@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 -> d@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmbA-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 -> e@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaZ-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 -> f@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaZ-0005vi-00" +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed + +******** SERVER ******** +1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D +1999-03-02 09:44:33 New connection +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp L S=sss id=E10HmaX-0005vi-00@myhost.test.ex for A@test.ex +1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F= temporarily rejected RCPT : 452 4.5.3 Try again +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp L S=sss id=E10HmaX-0005vi-00@myhost.test.ex for B@test.ex E@test.ex F@test.ex +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed +1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp L S=sss id=E10HmaX-0005vi-00@myhost.test.ex for C@test.ex D@test.ex +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: R=server +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed diff --git a/test/scripts/0000-Basic/0622 b/test/scripts/0000-Basic/0622 new file mode 100644 index 000000000..096980b80 --- /dev/null +++ b/test/scripts/0000-Basic/0622 @@ -0,0 +1,17 @@ +# PIPELINING and 250 after 452 in a set of RCPT responses +need_ipv4 +# +exim -DSERVER=server -bd -oX PORT_D +**** +exim -odq -t +To: A@test.ex,B@test.ex,C@test.ex,D@test.ex,E@test.ex,F@test.ex + +Testing +**** +exim -M $msg1 +**** +# +# +killdaemon +no_msglog_check +sleep 1 diff --git a/test/stderr/0143 b/test/stderr/0143 index f7f14f6ec..73ae66765 100644 --- a/test/stderr/0143 +++ b/test/stderr/0143 @@ -34,7 +34,7 @@ cmd buf flush ddd bytes SMTP<< 250 Sender OK SMTP>> RCPT TO: cmd buf flush ddd bytes -sync_responses expect rcpt +sync_responses expect rcpt for userx@domain.com SMTP<< 250 Recipient OK SMTP>> DATA cmd buf flush ddd bytes diff --git a/test/stderr/0398 b/test/stderr/0398 index 42893ad6a..d4f6a1c54 100644 --- a/test/stderr/0398 +++ b/test/stderr/0398 @@ -142,7 +142,7 @@ cmd buf flush ddd bytes SMTP<< 250 OK SMTP>> RCPT TO: cmd buf flush ddd bytes -sync_responses expect rcpt +sync_responses expect rcpt for qq@remote SMTP<< 550 Unknown SMTP>> QUIT cmd buf flush ddd bytes diff --git a/test/stderr/0432 b/test/stderr/0432 index 164a60473..da8c0b57c 100644 --- a/test/stderr/0432 +++ b/test/stderr/0432 @@ -104,7 +104,7 @@ cmd buf flush ddd bytes SMTP<< 250 OK SMTP>> RCPT TO: cmd buf flush ddd bytes -sync_responses expect rcpt +sync_responses expect rcpt for x@y SMTP<< 250 OK SMTP>> QUIT cmd buf flush ddd bytes diff --git a/test/stderr/0476 b/test/stderr/0476 index 2ea100bdc..391c13b59 100644 --- a/test/stderr/0476 +++ b/test/stderr/0476 @@ -33,7 +33,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for userx@test.ex SMTP<< 550 NO sync_responses expect data SMTP(closed)<< diff --git a/test/stderr/2035 b/test/stderr/2035 index 75c7a82f8..2c9f064a4 100644 --- a/test/stderr/2035 +++ b/test/stderr/2035 @@ -30,7 +30,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for userb@test.ex SMTP<< 250 Accepted LOG: MAIN <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss diff --git a/test/stderr/2135 b/test/stderr/2135 index 75c7a82f8..2c9f064a4 100644 --- a/test/stderr/2135 +++ b/test/stderr/2135 @@ -30,7 +30,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for userb@test.ex SMTP<< 250 Accepted LOG: MAIN <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss diff --git a/test/stderr/4052 b/test/stderr/4052 index 22f05589b..49bee93f2 100644 --- a/test/stderr/4052 +++ b/test/stderr/4052 @@ -43,7 +43,7 @@ EHLO cleartext extensions changed, 0x0120/0x0000 -> 0x0160/0x0000 writing clr 0160/0000 cry 0000/0000 sync_responses expect mail SMTP<< 250 mail-from accepted -sync_responses expect rcpt +sync_responses expect rcpt for extchange@test.ex SMTP<< 250 rcpt-to accepted sync_responses expect data SMTP<< 354 data go-ahead diff --git a/test/stderr/5410 b/test/stderr/5410 index 03876629e..6e82c6ada 100644 --- a/test/stderr/5410 +++ b/test/stderr/5410 @@ -127,7 +127,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for userx@domain.com SMTP<< 250 Accepted holding verify callout open for cutthrough delivery ----------- end cutthrough setup ------------ @@ -369,7 +369,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for usery@domain.com SMTP<< 250 Accepted holding verify callout open for cutthrough delivery ----------- end cutthrough setup ------------ @@ -611,7 +611,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for usery@domain.com SMTP<< 250 Accepted holding verify callout open for cutthrough delivery ----------- end cutthrough setup ------------ diff --git a/test/stderr/5420 b/test/stderr/5420 index 9504cd3ba..5d7097930 100644 --- a/test/stderr/5420 +++ b/test/stderr/5420 @@ -128,7 +128,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for userx@domain.com SMTP<< 250 Accepted holding verify callout open for cutthrough delivery ----------- end cutthrough setup ------------ @@ -370,7 +370,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for usery@domain.com SMTP<< 250 Accepted holding verify callout open for cutthrough delivery ----------- end cutthrough setup ------------ @@ -612,7 +612,7 @@ not using DSN cmd buf flush ddd bytes sync_responses expect mail SMTP<< 250 OK -sync_responses expect rcpt +sync_responses expect rcpt for usery@domain.com SMTP<< 250 Accepted holding verify callout open for cutthrough delivery ----------- end cutthrough setup ------------ -- 2.30.2