From a5aec58726beae3926bcbe2fed83953ea3cd636e Mon Sep 17 00:00:00 2001 From: Steve Holme Date: Sat, 5 Mar 2016 20:10:11 +0000 Subject: [PATCH] imap/pop3/smtp: Fixed connections upgraded with TLS are not reused Regression since commit 710f14edba. Bug: https://github.com/curl/curl/issues/422 Reported-by: Justin Ehlert --- lib/imap.c | 10 +++- lib/pop3.c | 10 +++- lib/smtp.c | 10 +++- lib/url.c | 127 +++++++++++++++++++++++++++++++++++++++++++++++--- lib/urldata.h | 1 + 5 files changed, 149 insertions(+), 9 deletions(-) diff --git a/lib/imap.c b/lib/imap.c index c75ccf9ad..0339add48 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -227,7 +227,11 @@ static const struct SASLproto saslimap = { #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->tls_upgraded = TRUE; } #else #define imap_to_imaps(x) Curl_nop_stmt @@ -1738,6 +1742,10 @@ static CURLcode imap_setup_connection(struct connectdata *conn) if(result) return result; + /* Clear the TLS upgraded flag */ + conn->tls_upgraded = FALSE; + + /* Set up the proxy if necessary */ if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { /* Unless we have asked to tunnel IMAP operations through the proxy, we switch and use HTTP operations only */ diff --git a/lib/pop3.c b/lib/pop3.c index 73e1cb2e4..cb53b7445 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -225,7 +225,11 @@ static const struct SASLproto saslpop3 = { #ifdef USE_SSL static void pop3_to_pop3s(struct connectdata *conn) { + /* Change the connection handler */ conn->handler = &Curl_handler_pop3s; + + /* Set the connection's upgraded to TLS flag */ + conn->tls_upgraded = TRUE; } #else #define pop3_to_pop3s(x) Curl_nop_stmt @@ -1354,6 +1358,10 @@ static CURLcode pop3_setup_connection(struct connectdata *conn) if(result) return result; + /* Clear the TLS upgraded flag */ + conn->tls_upgraded = FALSE; + + /* Set up the proxy if necessary */ if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { /* Unless we have asked to tunnel POP3 operations through the proxy, we switch and use HTTP operations only */ diff --git a/lib/smtp.c b/lib/smtp.c index 7102ffbf3..2a87c6eae 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2015, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -224,7 +224,11 @@ static const struct SASLproto saslsmtp = { #ifdef USE_SSL static void smtp_to_smtps(struct connectdata *conn) { + /* Change the connection handler */ conn->handler = &Curl_handler_smtps; + + /* Set the connection's upgraded to TLS flag */ + conn->tls_upgraded = TRUE; } #else #define smtp_to_smtps(x) Curl_nop_stmt @@ -1450,6 +1454,10 @@ static CURLcode smtp_setup_connection(struct connectdata *conn) struct SessionHandle *data = conn->data; CURLcode result; + /* Clear the TLS upgraded flag */ + conn->tls_upgraded = FALSE; + + /* Set up the proxy if necessary */ if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) { /* Unless we have asked to tunnel SMTP operations through the proxy, we switch and use HTTP operations only */ diff --git a/lib/url.c b/lib/url.c index 15bababbf..277c4cced 100644 --- a/lib/url.c +++ b/lib/url.c @@ -151,6 +151,8 @@ static CURLcode parse_url_login(struct SessionHandle *data, static CURLcode parse_login_details(const char *login, const size_t len, char **userptr, char **passwdptr, char **optionsptr); +static unsigned int get_protocol_family(unsigned int protocol); + /* * Protocol table. */ @@ -3272,7 +3274,8 @@ ConnectionExists(struct SessionHandle *data, if((needle->handler->flags&PROTOPT_SSL) != (check->handler->flags&PROTOPT_SSL)) /* don't do mixed SSL and non-SSL connections */ - if(!(needle->handler->protocol & check->handler->protocol)) + if(get_protocol_family(check->handler->protocol) != + needle->handler->protocol || !check->tls_upgraded) /* except protocols that have been upgraded via TLS */ continue; @@ -3327,14 +3330,16 @@ ConnectionExists(struct SessionHandle *data, Curl_raw_equal(needle->proxy.name, check->proxy.name) && (needle->port == check->port))) { /* The requested connection does not use a HTTP proxy or it uses SSL or - it is a non-SSL protocol tunneled over the same http proxy name and - port number or it is a non-SSL protocol which is allowed to be - upgraded via TLS */ - + it is a non-SSL protocol tunneled over the same HTTP proxy name and + port number */ if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) || - needle->handler->protocol & check->handler->protocol) && + (get_protocol_family(check->handler->protocol) == + needle->handler->protocol && check->tls_upgraded)) && Curl_raw_equal(needle->host.name, check->host.name) && needle->remote_port == check->remote_port) { + /* The schemes match or the the protocol family is the same and the + previous connection was TLS upgraded, and the hostname and host + port match */ if(needle->handler->flags & PROTOPT_SSL) { /* This is a SSL connection so verify that we're using the same SSL options as well */ @@ -6362,3 +6367,113 @@ CURLcode Curl_do_more(struct connectdata *conn, int *complete) return result; } + +/* +* get_protocol_family() +* +* This is used to return the protocol family for a given protocol. +* +* Parameters: +* +* protocol [in] - A single bit protocol identifier such as HTTP or HTTPS. +* +* Returns the family as a single bit protocol identifier. +*/ + +unsigned int get_protocol_family(unsigned int protocol) +{ + unsigned int family; + + switch(protocol) { + case CURLPROTO_HTTP: + case CURLPROTO_HTTPS: + family = CURLPROTO_HTTP; + break; + + case CURLPROTO_FTP: + case CURLPROTO_FTPS: + family = CURLPROTO_IMAP; + break; + + case CURLPROTO_SCP: + family = CURLPROTO_SCP; + break; + + case CURLPROTO_SFTP: + family = CURLPROTO_SFTP; + break; + + case CURLPROTO_TELNET: + family = CURLPROTO_TELNET; + break; + + case CURLPROTO_LDAP: + case CURLPROTO_LDAPS: + family = CURLPROTO_IMAP; + break; + + case CURLPROTO_DICT: + family = CURLPROTO_DICT; + break; + + case CURLPROTO_FILE: + family = CURLPROTO_FILE; + break; + + case CURLPROTO_TFTP: + family = CURLPROTO_TFTP; + break; + + case CURLPROTO_IMAP: + case CURLPROTO_IMAPS: + family = CURLPROTO_IMAP; + break; + + case CURLPROTO_POP3: + case CURLPROTO_POP3S: + family = CURLPROTO_POP3; + break; + + case CURLPROTO_SMTP: + case CURLPROTO_SMTPS: + family = CURLPROTO_SMTP; + break; + + case CURLPROTO_RTSP: + family = CURLPROTO_RTSP; + break; + + case CURLPROTO_RTMP: + case CURLPROTO_RTMPS: + family = CURLPROTO_RTMP; + break; + + case CURLPROTO_RTMPT: + case CURLPROTO_RTMPTS: + family = CURLPROTO_RTMPT; + break; + + case CURLPROTO_RTMPE: + family = CURLPROTO_RTMPE; + break; + + case CURLPROTO_RTMPTE: + family = CURLPROTO_RTMPTE; + break; + + case CURLPROTO_GOPHER: + family = CURLPROTO_GOPHER; + break; + + case CURLPROTO_SMB: + case CURLPROTO_SMBS: + family = CURLPROTO_SMB; + break; + + default: + family = 0; + break; + } + + return family; +} \ No newline at end of file diff --git a/lib/urldata.h b/lib/urldata.h index fcb17561d..f832ea886 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -921,6 +921,7 @@ struct connectdata { struct ssl_connect_data ssl[2]; /* this is for ssl-stuff */ struct ssl_config_data ssl_config; + bool tls_upgraded; struct ConnectBits bits; /* various state-flags for this connection */