commit 01179f2c11f78a72f59bb606a6ba5f73c22a7c09
parent 0d957a26a09098e6c2f642cd75f95eb9404201bd
Author: Andrej Kacian <ticho@claws-mail.org>
Date: Fri, 21 Dec 2018 10:21:10 +0100
Add Server Name Indication support to TLS connections, if applicable.
Adds a hidden pref "use_tls_sni".
Patch by Alex Smith.
Closes bug #4103: TLS SNI (Server Name Indication) support for IMAP, POP & SMTP
Diffstat:
16 files changed, 72 insertions(+), 0 deletions(-)
diff --git a/AUTHORS b/AUTHORS
@@ -325,3 +325,4 @@ contributors (in addition to the above; based on Changelog)
Michael Schwendt
Eric S. Raymond
Avinash Sonawane
+ Alex Smith
diff --git a/src/common/session.c b/src/common/session.c
@@ -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->use_tls_sni = TRUE;
#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;
+ sock->use_tls_sni = session->use_tls_sni;
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->use_tls_sni = session->use_tls_sni;
if (nb_mode)
sock_set_nonblocking_mode(session->sock, FALSE);
diff --git a/src/common/session.h b/src/common/session.h
@@ -160,6 +160,7 @@ struct _Session
#ifdef USE_GNUTLS
SSLType ssl_type;
gchar *gnutls_priority;
+ gboolean use_tls_sni;
#endif
};
diff --git a/src/common/socket.h b/src/common/socket.h
@@ -84,6 +84,7 @@ struct _SockInfo
const void *account;
gboolean is_smtp;
gboolean ssl_cert_auto_accept;
+ gboolean use_tls_sni;
};
void refresh_resolvers (void);
diff --git a/src/common/ssl.c b/src/common/ssl.c
@@ -410,6 +410,20 @@ gboolean ssl_init_socket(SockInfo *sockinfo)
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()) {
diff --git a/src/common/utils.c b/src/common/utils.c
@@ -1908,6 +1908,29 @@ const gchar *get_domain_name(void)
#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
diff --git a/src/common/utils.h b/src/common/utils.h
@@ -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);
+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);
diff --git a/src/etpan/etpan-ssl.c b/src/etpan/etpan-ssl.c
@@ -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);
}
+
+#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 */
diff --git a/src/gtk/authors.h b/src/gtk/authors.h
@@ -283,6 +283,7 @@ static char *CONTRIBS_LIST[] = {
"shigeri",
"Jesse Skinner",
"Ville Skyttä",
+"Alex Smith",
"Dale P. Smith",
"Avinash Sonawane",
"Andrea Spadaccini",
diff --git a/src/imap.c b/src/imap.c
@@ -1277,7 +1277,9 @@ static IMAPSession *imap_session_new(Folder * folder,
session->uidplus = FALSE;
session->cmd_count = 1;
}
+ SESSION(session)->use_tls_sni = account->use_tls_sni;
#endif
+
log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
(session->authenticated) ? "pre" : "un");
diff --git a/src/news.c b/src/news.c
@@ -361,6 +361,7 @@ static Session *news_session_new(Folder *folder, const PrefsAccount *account, gu
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
diff --git a/src/plugins/managesieve/managesieve.c b/src/plugins/managesieve/managesieve.c
@@ -1058,6 +1058,7 @@ static void sieve_session_reset(SieveSession *session)
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;
diff --git 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);
+ SESSION(session)->use_tls_sni = account->use_tls_sni;
#endif
session->state = POP3_READY;
diff --git a/src/prefs_account.c b/src/prefs_account.c
@@ -788,6 +788,9 @@ static PrefParam ssl_param[] = {
&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},
diff --git a/src/prefs_account.h b/src/prefs_account.h
@@ -86,6 +86,7 @@ struct _PrefsAccount
gboolean ssl_certs_auto_accept;
gboolean use_nonblocking_ssl;
+ gboolean use_tls_sni;
/* Receive */
gboolean use_apop_auth;
diff --git a/src/send_message.c b/src/send_message.c
@@ -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);
+ session->use_tls_sni = ac_prefs->use_tls_sni;
#else
if (ac_prefs->ssl_smtp != SSL_NONE) {
if (alertpanel_full(_("Insecure connection"),