imap: TLS upgrade fix
There were two places in the code that tried to connect the SSL filter,
e.g. do the TLS handshake, but only one changed imap state to CAPA
afterwards.
Depending on timing, the wrong path was taken and the connection was
hanging, waiting for a server reply to a command not sent.
Do the upgrade to tls in one place and update connection filter and
smtps protocol handler at the same time. Always transition to CAPA on
success.
Closes #16213
diff --git a/lib/imap.c b/lib/imap.c
index e5ee401..49abaf4 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -193,19 +193,6 @@
};
-#ifdef USE_SSL
-static void imap_to_imaps(struct connectdata *conn)
-{
- /* Change the connection handler */
- conn->handler = &Curl_handler_imaps;
-
- /* Set the connection's upgraded to TLS flag */
- conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define imap_to_imaps(x) Curl_nop_stmt
-#endif
-
/***********************************************************************
*
* imap_matchresp()
@@ -474,6 +461,7 @@
static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
struct connectdata *conn)
{
+#ifdef USE_SSL
/* Start the SSL connection */
struct imap_conn *imapc = &conn->proto.imapc;
CURLcode result;
@@ -483,21 +471,27 @@
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
+ /* Change the connection handler */
+ conn->handler = &Curl_handler_imaps;
+ conn->bits.tls_upgraded = TRUE;
}
+ DEBUGASSERT(!imapc->ssldone);
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- if(!result) {
+ DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d",
+ result, ssldone));
+ if(!result && ssldone) {
imapc->ssldone = ssldone;
- if(imapc->state != IMAP_UPGRADETLS)
- imap_state(data, IMAP_UPGRADETLS);
-
- if(imapc->ssldone) {
- imap_to_imaps(conn);
- result = imap_perform_capability(data, conn);
- }
+ /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */
+ result = imap_perform_capability(data, conn);
}
out:
return result;
+#else
+ (void)data;
+ (void)conn;
+ return CURLE_NOT_BUILT_IN;
+#endif
}
/***********************************************************************
@@ -998,7 +992,7 @@
result = imap_perform_authentication(data, conn);
}
else
- result = imap_perform_upgrade_tls(data, conn);
+ imap_state(data, IMAP_UPGRADETLS);
return result;
}
@@ -1307,8 +1301,12 @@
(void)data;
/* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
- if(imapc->state == IMAP_UPGRADETLS)
- return imap_perform_upgrade_tls(data, conn);
+upgrade_tls:
+ if(imapc->state == IMAP_UPGRADETLS) {
+ result = imap_perform_upgrade_tls(data, conn);
+ if(result || (imapc->state == IMAP_UPGRADETLS))
+ return result;
+ }
/* Flush any data that needs to be sent */
if(pp->sendleft)
@@ -1339,6 +1337,10 @@
case IMAP_STARTTLS:
result = imap_state_starttls_resp(data, imapcode, imapc->state);
+ /* During UPGRADETLS, leave the read loop as we need to connect
+ * (e.g. TLS handshake) before we continue sending/receiving. */
+ if(!result && (imapc->state == IMAP_UPGRADETLS))
+ goto upgrade_tls;
break;
case IMAP_AUTHENTICATE:
@@ -1392,14 +1394,6 @@
struct connectdata *conn = data->conn;
struct imap_conn *imapc = &conn->proto.imapc;
- if(Curl_conn_is_ssl(conn, FIRSTSOCKET) && !imapc->ssldone) {
- bool ssldone = FALSE;
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- imapc->ssldone = ssldone;
- if(result || !ssldone)
- return result;
- }
-
result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
*done = (imapc->state == IMAP_STOP);