Fix handling of server which follows a RCPT 452 with a 250. Bug 26092
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 6 Feb 2021 21:46:15 +0000 (21:46 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 6 Feb 2021 21:52:33 +0000 (21:52 +0000)
14 files changed:
doc/doc-txt/ChangeLog
src/src/transports/smtp.c
test/confs/0622 [new file with mode: 0644]
test/log/0622 [new file with mode: 0644]
test/scripts/0000-Basic/0622 [new file with mode: 0644]
test/stderr/0143
test/stderr/0398
test/stderr/0432
test/stderr/0476
test/stderr/2035
test/stderr/2135
test/stderr/4052
test/stderr/5410
test/stderr/5420

index 754ae7b49b6a83b6e074af54489f442b2dbda6cf..e4c1e9a2b590cbb09e5647d0af8d899cbba1c0f5 100644 (file)
@@ -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.
+
 
 
 
index 30669d35163a07e8a597c82a4dba24cebf6163a0..33051a5e2bac25cde2a8fead4e0aa5801311170f 100644 (file)
@@ -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 (file)
index 0000000..5dc3e0f
--- /dev/null
@@ -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 (file)
index 0000000..7f8edd2
--- /dev/null
@@ -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 <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 <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 <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 <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 <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 <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=<CALLER@myhost.test.ex> temporarily rejected RCPT <B@test.ex>: 452 4.5.3 Try again
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F=<CALLER@myhost.test.ex> temporarily rejected RCPT <C@test.ex>: 452 4.5.3 Try again
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F=<CALLER@myhost.test.ex> temporarily rejected RCPT <D@test.ex>: 452 4.5.3 Try again
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F=<CALLER@myhost.test.ex> temporarily rejected RCPT <E@test.ex>: 452 4.5.3 Try again
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F=<CALLER@myhost.test.ex> temporarily rejected RCPT <F@test.ex>: 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: <A@test.ex> 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=<CALLER@myhost.test.ex> temporarily rejected RCPT <C@test.ex>: 452 4.5.3 Try again
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] F=<CALLER@myhost.test.ex> temporarily rejected RCPT <D@test.ex>: 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: <F@test.ex> R=server
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: <E@test.ex> R=server
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => :blackhole: <B@test.ex> 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: <D@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <C@test.ex> 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 (file)
index 0000000..096980b
--- /dev/null
@@ -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
index f7f14f6ece1d7169174d6b8f7ff2fadbc4308f6f..73ae66765436ba7586af04aca446ff17247244ec 100644 (file)
@@ -34,7 +34,7 @@ cmd buf flush ddd bytes
   SMTP<< 250 Sender OK
   SMTP>> RCPT TO:<userx@domain.com>
 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
index 42893ad6a52c68f2e42313b344d90667f38ecb62..d4f6a1c5480167c7750eb6a07ea566fb31633f29 100644 (file)
@@ -142,7 +142,7 @@ cmd buf flush ddd bytes
   SMTP<< 250 OK
   SMTP>> RCPT TO:<qq@remote>
 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
index 164a60473cc2ae3908f6e222112863244fbca26a..da8c0b57c5b3794aacd379841126da370990c33d 100644 (file)
@@ -104,7 +104,7 @@ cmd buf flush ddd bytes
   SMTP<< 250 OK
   SMTP>> RCPT TO:<x@y>
 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
index 2ea100bdc8b7ec93fd06928cf12af88dad72e466..391c13b594fe586824774c3cd8fe6d2d11888904 100644 (file)
@@ -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)<<
index 75c7a82f867bfbc2e9c32a288d8f1458a7f6b8e1..2c9f064a494dd2264e8c94bc117b0ac4bd152660 100644 (file)
@@ -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
index 75c7a82f867bfbc2e9c32a288d8f1458a7f6b8e1..2c9f064a494dd2264e8c94bc117b0ac4bd152660 100644 (file)
@@ -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
index 22f05589b40a6bcb6c91cb028191ff7c4e9b33da..49bee93f2957d06f2e160fa9a35c638aa9cdb32b 100644 (file)
@@ -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
index 03876629ee05187ffee535d76df4ec65c52a3cb7..6e82c6adaa554227c1fb0f4b21ec3bfa775c58f2 100644 (file)
@@ -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 ------------
index 9504cd3baea96aa827991f0ef8bac619946543c2..5d70979304b3527c3c1d5cbdfca0be119e7ed8b2 100644 (file)
@@ -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 ------------