5ede3e
From a1ec463c8207bde97b3575d12e396e999a55a8d0 Mon Sep 17 00:00:00 2001
5ede3e
From: Patrick Monnerat <patrick@monnerat.net>
5ede3e
Date: Tue, 7 Sep 2021 13:26:42 +0200
5ede3e
Subject: [PATCH] ftp,imap,pop3,smtp: reject STARTTLS server response
5ede3e
 pipelining
5ede3e
5ede3e
If a server pipelines future responses within the STARTTLS response, the
5ede3e
former are preserved in the pingpong cache across TLS negotiation and
5ede3e
used as responses to the encrypted commands.
5ede3e
5ede3e
This fix detects pipelined STARTTLS responses and rejects them with an
5ede3e
error.
5ede3e
5ede3e
CVE-2021-22947
5ede3e
5ede3e
Bug: https://curl.se/docs/CVE-2021-22947.html
5ede3e
5ede3e
Upstream-commit: 8ef147c43646e91fdaad5d0e7b60351f842e5c68
5ede3e
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
5ede3e
---
5ede3e
 lib/ftp.c               |  3 +++
5ede3e
 lib/imap.c              |  4 +++
5ede3e
 lib/pop3.c              |  4 +++
5ede3e
 lib/smtp.c              |  4 +++
5ede3e
 tests/data/Makefile.inc |  2 +-
5ede3e
 tests/data/test980      | 52 ++++++++++++++++++++++++++++++++++++
5ede3e
 tests/data/test981      | 59 +++++++++++++++++++++++++++++++++++++++++
5ede3e
 tests/data/test982      | 57 +++++++++++++++++++++++++++++++++++++++
5ede3e
 tests/data/test983      | 52 ++++++++++++++++++++++++++++++++++++
5ede3e
 9 files changed, 236 insertions(+), 1 deletion(-)
5ede3e
 create mode 100644 tests/data/test980
5ede3e
 create mode 100644 tests/data/test981
5ede3e
 create mode 100644 tests/data/test982
5ede3e
 create mode 100644 tests/data/test983
5ede3e
5ede3e
diff --git a/lib/ftp.c b/lib/ftp.c
5ede3e
index 71f998e..e920138 100644
5ede3e
--- a/lib/ftp.c
5ede3e
+++ b/lib/ftp.c
5ede3e
@@ -2688,6 +2688,9 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
5ede3e
     case FTP_AUTH:
5ede3e
       /* we have gotten the response to a previous AUTH command */
5ede3e
 
5ede3e
+      if(pp->cache_size)
5ede3e
+        return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
5ede3e
+
5ede3e
       /* RFC2228 (page 5) says:
5ede3e
        *
5ede3e
        * If the server is willing to accept the named security mechanism,
5ede3e
diff --git a/lib/imap.c b/lib/imap.c
5ede3e
index feb7445..09bc5d6 100644
5ede3e
--- a/lib/imap.c
5ede3e
+++ b/lib/imap.c
5ede3e
@@ -939,6 +939,10 @@ static CURLcode imap_state_starttls_resp(struct connectdata *conn,
5ede3e
 
5ede3e
   (void)instate; /* no use for this yet */
5ede3e
 
5ede3e
+  /* Pipelining in response is forbidden. */
5ede3e
+  if(conn->proto.imapc.pp.cache_size)
5ede3e
+    return CURLE_WEIRD_SERVER_REPLY;
5ede3e
+
5ede3e
   if(imapcode != IMAP_RESP_OK) {
5ede3e
     if(data->set.use_ssl != CURLUSESSL_TRY) {
5ede3e
       failf(data, "STARTTLS denied");
5ede3e
diff --git a/lib/pop3.c b/lib/pop3.c
5ede3e
index 7698d1c..dccfced 100644
5ede3e
--- a/lib/pop3.c
5ede3e
+++ b/lib/pop3.c
5ede3e
@@ -750,6 +750,10 @@ static CURLcode pop3_state_starttls_resp(struct connectdata *conn,
5ede3e
 
5ede3e
   (void)instate; /* no use for this yet */
5ede3e
 
5ede3e
+  /* Pipelining in response is forbidden. */
5ede3e
+  if(conn->proto.pop3c.pp.cache_size)
5ede3e
+    return CURLE_WEIRD_SERVER_REPLY;
5ede3e
+
5ede3e
   if(pop3code != '+') {
5ede3e
     if(data->set.use_ssl != CURLUSESSL_TRY) {
5ede3e
       failf(data, "STARTTLS denied");
5ede3e
diff --git a/lib/smtp.c b/lib/smtp.c
5ede3e
index 1defb25..1f89777 100644
5ede3e
--- a/lib/smtp.c
5ede3e
+++ b/lib/smtp.c
5ede3e
@@ -685,6 +685,10 @@ static CURLcode smtp_state_starttls_resp(struct connectdata *conn,
5ede3e
 
5ede3e
   (void)instate; /* no use for this yet */
5ede3e
 
5ede3e
+  /* Pipelining in response is forbidden. */
5ede3e
+  if(conn->proto.smtpc.pp.cache_size)
5ede3e
+    return CURLE_WEIRD_SERVER_REPLY;
5ede3e
+
5ede3e
   if(smtpcode != 220) {
5ede3e
     if(data->set.use_ssl != CURLUSESSL_TRY) {
5ede3e
       failf(data, "STARTTLS denied, code %d", smtpcode);
5ede3e
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
5ede3e
index 163ce59..42b0569 100644
5ede3e
--- a/tests/data/Makefile.inc
5ede3e
+++ b/tests/data/Makefile.inc
5ede3e
@@ -108,7 +108,7 @@ test927 test928 test929 test930 test931 test932 test933 test934 test935 \
5ede3e
 test936 test937 test938 test939 test940 test941 test942 test943 test944 \
5ede3e
 test945 test946 test947 test948 test949 test950 test951 test952 \
5ede3e
 \
5ede3e
-test984 test985 test986 \
5ede3e
+test980 test981 test982 test983 test984 test985 test986 \
5ede3e
 \
5ede3e
 test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \
5ede3e
 test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \
5ede3e
diff --git a/tests/data/test980 b/tests/data/test980
5ede3e
new file mode 100644
5ede3e
index 0000000..97567f8
5ede3e
--- /dev/null
5ede3e
+++ b/tests/data/test980
5ede3e
@@ -0,0 +1,52 @@
5ede3e
+<testcase>
5ede3e
+<info>
5ede3e
+<keywords>
5ede3e
+SMTP
5ede3e
+STARTTLS
5ede3e
+</keywords>
5ede3e
+</info>
5ede3e
+
5ede3e
+#
5ede3e
+# Server-side
5ede3e
+<reply>
5ede3e
+<servercmd>
5ede3e
+CAPA STARTTLS
5ede3e
+AUTH PLAIN
5ede3e
+REPLY STARTTLS 454 currently unavailable\r\n235 Authenticated\r\n250 2.1.0 Sender ok\r\n250 2.1.5 Recipient ok\r\n354 Enter mail\r\n250 2.0.0 Accepted
5ede3e
+REPLY AUTH 535 5.7.8 Authentication credentials invalid
5ede3e
+</servercmd>
5ede3e
+</reply>
5ede3e
+
5ede3e
+#
5ede3e
+# Client-side
5ede3e
+<client>
5ede3e
+<features>
5ede3e
+SSL
5ede3e
+</features>
5ede3e
+<server>
5ede3e
+smtp
5ede3e
+</server>
5ede3e
+ <name>
5ede3e
+SMTP STARTTLS pipelined server response
5ede3e
+ </name>
5ede3e
+<stdin>
5ede3e
+mail body
5ede3e
+</stdin>
5ede3e
+ <command>
5ede3e
+smtp://%HOSTIP:%SMTPPORT/%TESTNUMBER --mail-rcpt recipient@example.com --mail-from sender@example.com -u user:secret --ssl --sasl-ir -T -
5ede3e
+</command>
5ede3e
+</client>
5ede3e
+
5ede3e
+#
5ede3e
+# Verify data after the test has been "shot"
5ede3e
+<verify>
5ede3e
+# 8 is CURLE_WEIRD_SERVER_REPLY
5ede3e
+<errorcode>
5ede3e
+8
5ede3e
+</errorcode>
5ede3e
+<protocol>
5ede3e
+EHLO %TESTNUMBER
5ede3e
+STARTTLS
5ede3e
+</protocol>
5ede3e
+</verify>
5ede3e
+</testcase>
5ede3e
diff --git a/tests/data/test981 b/tests/data/test981
5ede3e
new file mode 100644
5ede3e
index 0000000..2b98ce4
5ede3e
--- /dev/null
5ede3e
+++ b/tests/data/test981
5ede3e
@@ -0,0 +1,59 @@
5ede3e
+<testcase>
5ede3e
+<info>
5ede3e
+<keywords>
5ede3e
+IMAP
5ede3e
+STARTTLS
5ede3e
+</keywords>
5ede3e
+</info>
5ede3e
+
5ede3e
+#
5ede3e
+# Server-side
5ede3e
+<reply>
5ede3e
+<servercmd>
5ede3e
+CAPA STARTTLS
5ede3e
+REPLY STARTTLS A002 BAD currently unavailable\r\nA003 OK Authenticated\r\nA004 OK Accepted
5ede3e
+REPLY LOGIN A003 BAD Authentication credentials invalid
5ede3e
+</servercmd>
5ede3e
+</reply>
5ede3e
+
5ede3e
+#
5ede3e
+# Client-side
5ede3e
+<client>
5ede3e
+<features>
5ede3e
+SSL
5ede3e
+</features>
5ede3e
+<server>
5ede3e
+imap
5ede3e
+</server>
5ede3e
+ <name>
5ede3e
+IMAP STARTTLS pipelined server response
5ede3e
+ </name>
5ede3e
+ <command>
5ede3e
+imap://%HOSTIP:%IMAPPORT/%TESTNUMBER -T log/upload%TESTNUMBER -u user:secret --ssl
5ede3e
+</command>
5ede3e
+<file name="log/upload%TESTNUMBER">
5ede3e
+Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST)
5ede3e
+From: Fred Foobar <foobar@example.COM>
5ede3e
+Subject: afternoon meeting
5ede3e
+To: joe@example.com
5ede3e
+Message-Id: <B27397-0100000@example.COM>
5ede3e
+MIME-Version: 1.0
5ede3e
+Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
5ede3e
+
5ede3e
+Hello Joe, do you think we can meet at 3:30 tomorrow?
5ede3e
+</file>
5ede3e
+</client>
5ede3e
+
5ede3e
+#
5ede3e
+# Verify data after the test has been "shot"
5ede3e
+<verify>
5ede3e
+# 8 is CURLE_WEIRD_SERVER_REPLY
5ede3e
+<errorcode>
5ede3e
+8
5ede3e
+</errorcode>
5ede3e
+<protocol>
5ede3e
+A001 CAPABILITY
5ede3e
+A002 STARTTLS
5ede3e
+</protocol>
5ede3e
+</verify>
5ede3e
+</testcase>
5ede3e
diff --git a/tests/data/test982 b/tests/data/test982
5ede3e
new file mode 100644
5ede3e
index 0000000..9e07cc0
5ede3e
--- /dev/null
5ede3e
+++ b/tests/data/test982
5ede3e
@@ -0,0 +1,57 @@
5ede3e
+<testcase>
5ede3e
+<info>
5ede3e
+<keywords>
5ede3e
+POP3
5ede3e
+STARTTLS
5ede3e
+</keywords>
5ede3e
+</info>
5ede3e
+
5ede3e
+#
5ede3e
+# Server-side
5ede3e
+<reply>
5ede3e
+<servercmd>
5ede3e
+CAPA STLS USER
5ede3e
+REPLY STLS -ERR currently unavailable\r\n+OK user accepted\r\n+OK authenticated
5ede3e
+REPLY PASS -ERR Authentication credentials invalid
5ede3e
+</servercmd>
5ede3e
+<data nocheck="yes">
5ede3e
+From: me@somewhere
5ede3e
+To: fake@nowhere
5ede3e
+
5ede3e
+body
5ede3e
+
5ede3e
+--
5ede3e
+  yours sincerely
5ede3e
+</data>
5ede3e
+</reply>
5ede3e
+
5ede3e
+#
5ede3e
+# Client-side
5ede3e
+<client>
5ede3e
+<features>
5ede3e
+SSL
5ede3e
+</features>
5ede3e
+<server>
5ede3e
+pop3
5ede3e
+</server>
5ede3e
+ <name>
5ede3e
+POP3 STARTTLS pipelined server response
5ede3e
+ </name>
5ede3e
+ <command>
5ede3e
+pop3://%HOSTIP:%POP3PORT/%TESTNUMBER -u user:secret --ssl
5ede3e
+ </command>
5ede3e
+</client>
5ede3e
+
5ede3e
+#
5ede3e
+# Verify data after the test has been "shot"
5ede3e
+<verify>
5ede3e
+# 8 is CURLE_WEIRD_SERVER_REPLY
5ede3e
+<errorcode>
5ede3e
+8
5ede3e
+</errorcode>
5ede3e
+<protocol>
5ede3e
+CAPA
5ede3e
+STLS
5ede3e
+</protocol>
5ede3e
+</verify>
5ede3e
+</testcase>
5ede3e
diff --git a/tests/data/test983 b/tests/data/test983
5ede3e
new file mode 100644
5ede3e
index 0000000..300ec45
5ede3e
--- /dev/null
5ede3e
+++ b/tests/data/test983
5ede3e
@@ -0,0 +1,52 @@
5ede3e
+<testcase>
5ede3e
+<info>
5ede3e
+<keywords>
5ede3e
+FTP
5ede3e
+STARTTLS
5ede3e
+</keywords>
5ede3e
+</info>
5ede3e
+
5ede3e
+#
5ede3e
+# Server-side
5ede3e
+<reply>
5ede3e
+<servercmd>
5ede3e
+REPLY AUTH 500 unknown command\r\n500 unknown command\r\n331 give password\r\n230 Authenticated\r\n257 "/"\r\n200 OK\r\n200 OK\r\n200 OK\r\n226 Transfer complete
5ede3e
+REPLY PASS 530 Login incorrect
5ede3e
+</servercmd>
5ede3e
+</reply>
5ede3e
+
5ede3e
+# Client-side
5ede3e
+<client>
5ede3e
+<features>
5ede3e
+SSL
5ede3e
+</features>
5ede3e
+<server>
5ede3e
+ftp
5ede3e
+</server>
5ede3e
+ <name>
5ede3e
+FTP STARTTLS pipelined server response
5ede3e
+ </name>
5ede3e
+<file name="log/test%TESTNUMBER.txt">
5ede3e
+data
5ede3e
+    to
5ede3e
+      see
5ede3e
+that FTPS
5ede3e
+works
5ede3e
+  so does it?
5ede3e
+</file>
5ede3e
+ <command>
5ede3e
+--ssl --ftp-ssl-control ftp://%HOSTIP:%FTPPORT/%TESTNUMBER -T log/test%TESTNUMBER.txt -u user:secret -P %CLIENTIP
5ede3e
+</command>
5ede3e
+</client>
5ede3e
+
5ede3e
+# Verify data after the test has been "shot"
5ede3e
+<verify>
5ede3e
+# 8 is CURLE_WEIRD_SERVER_REPLY
5ede3e
+<errorcode>
5ede3e
+8
5ede3e
+</errorcode>
5ede3e
+<protocol>
5ede3e
+AUTH SSL
5ede3e
+</protocol>
5ede3e
+</verify>
5ede3e
+</testcase>
5ede3e
-- 
5ede3e
2.31.1
5ede3e