Add Server Name Indication support to TLS connections, if applicable.
authorAndrej Kacian <ticho@claws-mail.org>
Fri, 21 Dec 2018 09:21:10 +0000 (10:21 +0100)
committerAndrej Kacian <ticho@claws-mail.org>
Fri, 21 Dec 2018 09:29:46 +0000 (10:29 +0100)
Adds a hidden pref "use_tls_sni".

Patch by Alex Smith.
Closes bug #4103: TLS SNI (Server Name Indication) support for IMAP, POP & SMTP

16 files changed:
AUTHORS
src/common/session.c
src/common/session.h
src/common/socket.h
src/common/ssl.c
src/common/utils.c
src/common/utils.h
src/etpan/etpan-ssl.c
src/gtk/authors.h
src/imap.c
src/news.c
src/plugins/managesieve/managesieve.c
src/pop.c
src/prefs_account.c
src/prefs_account.h
src/send_message.c

diff --git a/AUTHORS b/AUTHORS
index e6eb90785956bc59dd4cd436ba8cc0100bcf2297..5c5d9f564df0d5d83b861a6a59767cfaded9ef55 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -325,3 +325,4 @@ contributors (in addition to the above; based on Changelog)
        Michael Schwendt
        Eric S. Raymond
        Avinash Sonawane
        Michael Schwendt
        Eric S. Raymond
        Avinash Sonawane
+       Alex Smith
index 1342ef947f48e4b66697c46488929667f1a028dc..2dc0a0a91e2581d09389c11fc9730d927c0d8e53 100644 (file)
@@ -69,6 +69,7 @@ void session_init(Session *session, const void *prefs_account, gboolean is_smtp)
        session->port = 0;
 #ifdef USE_GNUTLS
        session->ssl_type = SSL_NONE;
        session->port = 0;
 #ifdef USE_GNUTLS
        session->ssl_type = SSL_NONE;
+       session->use_tls_sni = TRUE;
 #endif
        session->nonblocking = TRUE;
        session->state = SESSION_READY;
 #endif
        session->nonblocking = TRUE;
        session->state = SESSION_READY;
@@ -194,6 +195,7 @@ static gint session_connect_cb(SockInfo *sock, gpointer data)
 
 #ifdef USE_GNUTLS
        sock->gnutls_priority = session->gnutls_priority;
 
 #ifdef USE_GNUTLS
        sock->gnutls_priority = session->gnutls_priority;
+       sock->use_tls_sni = session->use_tls_sni;
 
        if (session->ssl_type == SSL_TUNNEL) {
                sock_set_nonblocking_mode(sock, FALSE);
 
        if (session->ssl_type == SSL_TUNNEL) {
                sock_set_nonblocking_mode(sock, FALSE);
@@ -407,6 +409,7 @@ gint session_start_tls(Session *session)
 
        session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
        session->sock->gnutls_priority = session->gnutls_priority;
 
        session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
        session->sock->gnutls_priority = session->gnutls_priority;
+       session->sock->use_tls_sni = session->use_tls_sni;
 
        if (nb_mode)
                sock_set_nonblocking_mode(session->sock, FALSE);
 
        if (nb_mode)
                sock_set_nonblocking_mode(session->sock, FALSE);
index 5cd518b828bb50a13d3d57f23c97d0c9bf38304e..13a56c97ebeb4035fd7d2d118b8560eb4e13a438 100644 (file)
@@ -160,6 +160,7 @@ struct _Session
 #ifdef USE_GNUTLS
        SSLType ssl_type;
        gchar *gnutls_priority;
 #ifdef USE_GNUTLS
        SSLType ssl_type;
        gchar *gnutls_priority;
+       gboolean use_tls_sni;
 #endif
 };
 
 #endif
 };
 
index 2c78066d26d22ecebf5628ab6a0b1c788f0d17b0..005f5a0ad6c314c11bdae06f31c1bfba4346bbb5 100644 (file)
@@ -84,6 +84,7 @@ struct _SockInfo
        const void *account;
        gboolean is_smtp;
        gboolean ssl_cert_auto_accept;
        const void *account;
        gboolean is_smtp;
        gboolean ssl_cert_auto_accept;
+       gboolean use_tls_sni;
 };
 
 void refresh_resolvers                 (void);
 };
 
 void refresh_resolvers                 (void);
index a1438207be20e577fa6e354cb4dd288e187575ee..cc38c228d388fcf63d5265ca60480c6565349bf8 100644 (file)
@@ -410,6 +410,20 @@ gboolean ssl_init_socket(SockInfo *sockinfo)
 
        gnutls_record_disable_padding(session);
 
 
        gnutls_record_disable_padding(session);
 
+       /* If we have a host name, rather than a numerical IP address, tell
+        * gnutls to send it in the server name identification extension field,
+        * to give the server a chance to select the correct certificate in the
+        * virtual hosting case where multiple domain names are hosted on the
+        * same IP address. */
+       if (sockinfo->use_tls_sni &&
+                       sockinfo->hostname != NULL &&
+                       !is_numeric_host_address(sockinfo->hostname)) {
+               r = gnutls_server_name_set(session, GNUTLS_NAME_DNS,
+                               sockinfo->hostname, strlen(sockinfo->hostname));
+               debug_print("Set GnuTLS session server name indication to %s, status = %d\n",
+                           sockinfo->hostname, r);
+       }
+
        gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
 
        if (claws_ssl_get_cert_file()) {
        gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
 
        if (claws_ssl_get_cert_file()) {
index a815c48bb8f1e6fadc58f716426ef3de90595b2c..195193b3ed9d8a5733efa0098f7099b3a9b40388 100644 (file)
@@ -1908,6 +1908,29 @@ const gchar *get_domain_name(void)
 #endif
 }
 
 #endif
 }
 
+/* Tells whether the given host address string is a valid representation of a
+ * numerical IP (v4 or, if supported, v6) address.
+ */
+gboolean is_numeric_host_address(const gchar *hostaddress)
+{
+       struct addrinfo hints, *res;
+       int err;
+
+       /* See what getaddrinfo makes of the string when told that it is a
+        * numeric IP address representation. */
+       memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = 0;
+       hints.ai_flags = AI_NUMERICHOST;
+       hints.ai_protocol = 0;
+
+       err = getaddrinfo(hostaddress, NULL, &hints, &res);
+       if (err == 0)
+               freeaddrinfo(res);
+
+       return (err == 0);
+}
+
 off_t get_file_size(const gchar *file)
 {
 #ifdef G_OS_WIN32
 off_t get_file_size(const gchar *file)
 {
 #ifdef G_OS_WIN32
index 64a639f94c5de46281b91af5d6b305f098fad1b1..d51cc00e1567eb79f3c8c755a2416dde35e9e868 100644 (file)
@@ -395,6 +395,7 @@ const gchar *get_tmp_dir            (void);
 const gchar *get_locale_dir            (void);
 gchar *get_tmp_file                    (void);
 const gchar *get_domain_name           (void);
 const gchar *get_locale_dir            (void);
 gchar *get_tmp_file                    (void);
 const gchar *get_domain_name           (void);
+gboolean is_numeric_host_address       (const gchar *hostaddress);
 const gchar *get_desktop_file(void);
 #ifdef G_OS_WIN32
 const gchar *w32_get_themes_dir    (void);
 const gchar *get_desktop_file(void);
 #ifdef G_OS_WIN32
 const gchar *w32_get_themes_dir    (void);
index aeb5be9b62e3f490fa29dc58a904a3eee6796ccb..f8537892244116087d07f805bf358521c5708d1f 100644 (file)
@@ -171,6 +171,23 @@ void etpan_connect_ssl_context_cb(struct mailstream_ssl_context * ssl_context, v
                gnutls_x509_crt_deinit(x509);
                gnutls_x509_privkey_deinit(pkey);
        }
                gnutls_x509_crt_deinit(x509);
                gnutls_x509_privkey_deinit(pkey);
        }
+
+#if (defined LIBETPAN_API_CURRENT && LIBETPAN_API_CURRENT >= 23)
+       /* If we have a host name, rather than a numerical IP address, tell
+        * gnutls to send it in the Server Name Identification extension field,
+        * to give the server a chance to select the correct certificate in the
+        * virtual hosting case where multiple domain names are hosted on the
+        * same IP address. */
+       if (session->use_tls_sni &&
+                       !is_numeric_host_address(account->recv_server)) {
+               int r;
+
+               r = mailstream_ssl_set_server_name(ssl_context, account->recv_server);
+               debug_print("Set libetpan SSL mail stream server name indication to %s, status = %d\n",
+                           account->recv_server, r);
+       }
+#endif /* LIBETPAN_API_CURRENT >= 23 */
+
 }
 
 #endif /* USE_GNUTLS */
 }
 
 #endif /* USE_GNUTLS */
index 628f7d901fff68e4b2a81df1cbbaf8b04de91491..57650073c0a02443b509abae38fac1e8292f571d 100644 (file)
@@ -283,6 +283,7 @@ static char *CONTRIBS_LIST[] = {
 "shigeri",
 "Jesse Skinner",
 "Ville Skyttä",
 "shigeri",
 "Jesse Skinner",
 "Ville Skyttä",
+"Alex Smith",
 "Dale P. Smith",
 "Avinash Sonawane",
 "Andrea Spadaccini",
 "Dale P. Smith",
 "Avinash Sonawane",
 "Andrea Spadaccini",
index 9b78b796294e53abf83cac67527e49d1e67620f8..7124d832be0b297ba6ccf5744bbb066841b7bc43 100644 (file)
@@ -1277,7 +1277,9 @@ static IMAPSession *imap_session_new(Folder * folder,
                session->uidplus = FALSE;
                session->cmd_count = 1;
        }
                session->uidplus = FALSE;
                session->cmd_count = 1;
        }
+       SESSION(session)->use_tls_sni = account->use_tls_sni;
 #endif
 #endif
+
        log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
                    (session->authenticated) ? "pre" : "un");
        
        log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
                    (session->authenticated) ? "pre" : "un");
        
index 634a0508223f1ff60074f321f51fcc8400ed54bb..126db1592b7b5204544137827607a3e9b71bf632 100644 (file)
@@ -361,6 +361,7 @@ static Session *news_session_new(Folder *folder, const PrefsAccount *account, gu
        nntp_init(folder);
 
 #ifdef USE_GNUTLS
        nntp_init(folder);
 
 #ifdef USE_GNUTLS
+       SESSION(session)->use_tls_sni = account->use_tls_sni;
        if (ssl_type != SSL_NONE)
                r = nntp_threaded_connect_ssl(folder, server, port, proxy_info);
        else
        if (ssl_type != SSL_NONE)
                r = nntp_threaded_connect_ssl(folder, server, port, proxy_info);
        else
index 74b08d3eb109000852281158da0a0a90d150f0ef..d4d40e8377ce15f5c7b76f9d9cdcd93d30770906 100644 (file)
@@ -1058,6 +1058,7 @@ static void sieve_session_reset(SieveSession *session)
        session->state = SIEVE_CAPABILITIES;
 #ifdef USE_GNUTLS
        session->tls_init_done = FALSE;
        session->state = SIEVE_CAPABILITIES;
 #ifdef USE_GNUTLS
        session->tls_init_done = FALSE;
+       SESSION(session)->use_tls_sni = account->use_tls_sni;
 #endif
        session->avail_auth_type = 0;
        session->auth_type = 0;
 #endif
        session->avail_auth_type = 0;
        session->auth_type = 0;
index 6de10c6ca35a351139dd184424d658d1ead42295..2b8c70a6db00d1b5b528f0945dc0c332ed4944bf 100644 (file)
--- a/src/pop.c
+++ b/src/pop.c
@@ -538,6 +538,7 @@ Session *pop3_session_new(PrefsAccount *account)
        if (account->set_gnutls_priority && account->gnutls_priority &&
                        strlen(account->gnutls_priority) != 0)
                SESSION(session)->gnutls_priority = g_strdup(account->gnutls_priority);
        if (account->set_gnutls_priority && account->gnutls_priority &&
                        strlen(account->gnutls_priority) != 0)
                SESSION(session)->gnutls_priority = g_strdup(account->gnutls_priority);
+       SESSION(session)->use_tls_sni = account->use_tls_sni;
 #endif
 
        session->state = POP3_READY;
 #endif
 
        session->state = POP3_READY;
index 137ebbceecf9955ac4bce25156c0684f1b047da7..63bafb9baae5dcfe230685848ceaabc403291344 100644 (file)
@@ -788,6 +788,9 @@ static PrefParam ssl_param[] = {
         &ssl_page.use_nonblocking_ssl_checkbtn,
         prefs_set_data_from_toggle, prefs_set_toggle},
 
         &ssl_page.use_nonblocking_ssl_checkbtn,
         prefs_set_data_from_toggle, prefs_set_toggle},
 
+       {"use_tls_sni", "1", &tmp_ac_prefs.use_tls_sni, P_BOOL,
+        NULL, NULL, NULL},
+
        {"in_ssl_client_cert_file", "", &tmp_ac_prefs.in_ssl_client_cert_file, P_STRING,
         &ssl_page.entry_in_cert_file, prefs_set_data_from_entry, prefs_set_entry},
 
        {"in_ssl_client_cert_file", "", &tmp_ac_prefs.in_ssl_client_cert_file, P_STRING,
         &ssl_page.entry_in_cert_file, prefs_set_data_from_entry, prefs_set_entry},
 
index 6c956cf86da033dc37c7160469503a700a421464..4c53fea0743891177ad0f3e6292c5676251e8066 100644 (file)
@@ -86,6 +86,7 @@ struct _PrefsAccount
 
        gboolean ssl_certs_auto_accept;
        gboolean use_nonblocking_ssl;
 
        gboolean ssl_certs_auto_accept;
        gboolean use_nonblocking_ssl;
+       gboolean use_tls_sni;
 
        /* Receive */
        gboolean use_apop_auth;
 
        /* Receive */
        gboolean use_apop_auth;
index ce8b12525b2c3552a75364daa589f34239d194c7..820b097a144c60c8197b0ff90cd5f30d6b69fd60 100644 (file)
@@ -280,6 +280,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g
                if (ac_prefs->set_gnutls_priority && ac_prefs->gnutls_priority &&
                    strlen(ac_prefs->gnutls_priority))
                        session->gnutls_priority = g_strdup(ac_prefs->gnutls_priority);
                if (ac_prefs->set_gnutls_priority && ac_prefs->gnutls_priority &&
                    strlen(ac_prefs->gnutls_priority))
                        session->gnutls_priority = g_strdup(ac_prefs->gnutls_priority);
+               session->use_tls_sni = ac_prefs->use_tls_sni;
 #else
                if (ac_prefs->ssl_smtp != SSL_NONE) {
                        if (alertpanel_full(_("Insecure connection"),
 #else
                if (ac_prefs->ssl_smtp != SSL_NONE) {
                        if (alertpanel_full(_("Insecure connection"),