From 6300dfec71d376c96351708f76a6c4ee4a187eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 13:13:02 +0200 Subject: [PATCH 01/19] Always build irssi with TLS support. This patch removes the optional checks for whether to build irssi with TLS support or not. This will allow us to ship a default configuration file where we connect to TLS enabled IRC servers out of the box. --- NEWS | 1 + configure.ac | 51 +++++--------------------------------- src/core/network-openssl.c | 10 -------- src/core/servers.c | 4 --- 4 files changed, 7 insertions(+), 59 deletions(-) diff --git a/NEWS b/NEWS index a76771e7..ca6ecce3 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ v0.8.21-head 2016-xx-xx The Irssi team + autolog_ignore_targets and activity_hide_targets learn a new syntax tag/* and * to ignore whole networks or everything. + /hilight got a -matchcase flag to hilight case sensitively (#421). + + Always build irssi with TLS support. - IP addresses are no longer stored when resolve_reverse_lookup is used. - /names and $[...] now uses utf8 string operations (#40, #411). diff --git a/configure.ac b/configure.ac index ee03eb82..629dd590 100644 --- a/configure.ac +++ b/configure.ac @@ -153,14 +153,6 @@ AC_ARG_ENABLE(true-color, fi, want_truecolor=no) -dnl ** -dnl ** SSL Library checks (OpenSSL) -dnl ** - -AC_ARG_ENABLE(ssl, -[ --disable-ssl Disable Secure Sockets Layer support],, - enable_ssl=yes) - dnl ** dnl ** just some generic stuff... dnl ** @@ -238,6 +230,11 @@ if test "x$want_socks" = "xyes"; then ]) fi +dnl ** +dnl ** OpenSSL checks +dnl ** +AC_CHECK_LIB([ssl], [SSL_library_init]) + dnl ** dnl ** fe-text checks dnl ** @@ -279,27 +276,7 @@ if test -z "$GLIB_LIBS"; then AC_ERROR([GLIB is required to build irssi.]) fi -LIBS="$LIBS $GLIB_LIBS" - -have_openssl=no -if test "$enable_ssl" = "yes"; then - PKG_CHECK_MODULES(SSL, openssl, :, :) - if test "$SSL_LIBS"; then - CFLAGS="$CFLAGS $SSL_CFLAGS" - have_openssl=yes - else - AC_CHECK_LIB(ssl, SSL_read, [ - AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h, [ - SSL_LIBS="-lssl -lcrypto" - have_openssl=yes - ]) - ],, -lcrypto) - fi - if test "$have_openssl" = "yes"; then - AC_DEFINE(HAVE_OPENSSL,, Build with OpenSSL support) - LIBS="$LIBS $SSL_LIBS" - fi -fi +LIBS="$LIBS $GLIB_LIBS -lssl -lcrypto" dnl ** dnl ** curses checks @@ -569,12 +546,6 @@ if test "x$want_dane" = "xyes"; then AC_DEFINE([HAVE_DANE], [], [DANE support]) have_dane=yes ], [], [-lssl -lcrypto -lsres -lpthread]) - - if test x$have_dane = "xyes" ; then - if test x$have_openssl = "xno" ; then - AC_ERROR([SSL is required to build Irssi with DANE support enabled.]) - fi - fi fi if test "x$want_truecolor" = "xyes"; then @@ -695,16 +666,6 @@ echo "Install prefix ................... : $prefix" echo -echo "Building with SSL support ........ : $have_openssl" -if test "x$have_openssl" = "xno" -a "x$enable_ssl" = "xyes"; then - if test -f /etc/debian_version; then - echo " - Try: sudo apt-get install libssl-dev" - elif test -f /etc/redhat-release; then - echo " - Try installing openssl-devel" - else - echo " - Try installing OpenSSL development headers" - fi -fi echo "Building with 64bit DCC support .. : $offt_64bit" echo "Building with DANE support ....... : $have_dane" echo "Building with true color support.. : $want_truecolor" diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index a18e6fc7..014f0a28 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -23,8 +23,6 @@ #include "misc.h" #include "servers.h" -#ifdef HAVE_OPENSSL - #include #include #include @@ -620,13 +618,5 @@ int irssi_ssl_handshake(GIOChannel *handle) return ret ? 0 : -1; } -#else /* HAVE_OPENSSL */ -GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server) -{ - g_warning("Connection failed: SSL support not enabled in this build."); - errno = ENOSYS; - return NULL; -} -#endif /* ! HAVE_OPENSSL */ diff --git a/src/core/servers.c b/src/core/servers.c index d6297c4d..dfcbcde0 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -167,7 +167,6 @@ static void server_connect_callback_init(SERVER_REC *server, GIOChannel *handle) server_connect_finished(server); } -#ifdef HAVE_OPENSSL static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *handle) { int error; @@ -198,7 +197,6 @@ static void server_connect_callback_init_ssl(SERVER_REC *server, GIOChannel *han server_connect_finished(server); } -#endif static void server_real_connect(SERVER_REC *server, IPADDR *ip, const char *unix_socket) @@ -247,11 +245,9 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, g_free(errmsg2); } else { server->handle = net_sendbuffer_create(handle, 0); -#ifdef HAVE_OPENSSL if (server->connrec->use_ssl) server_connect_callback_init_ssl(server, handle); else -#endif server->connect_tag = g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ, (GInputFunction) From da67d3e8e69eb5fb702a3dd39356d38a1ee9d8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 13:20:14 +0200 Subject: [PATCH 02/19] Add function to convert a buffer to a colon-delimited hex string. This patch adds binary_to_hex(), which can take an input buffer and convert it to colon-delimited hex strings suitable for printing for fingerprints. --- src/core/misc.c | 20 ++++++++++++++++++++ src/core/misc.h | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/src/core/misc.c b/src/core/misc.c index bc9f504e..0bb1f7e6 100644 --- a/src/core/misc.c +++ b/src/core/misc.c @@ -930,3 +930,23 @@ char **strsplit_len(const char *str, int len, gboolean onspace) return ret; } + +char *binary_to_hex(unsigned char *buffer, size_t size) +{ + static const char hex[] = "0123456789ABCDEF"; + char *result = NULL; + int i; + + if (buffer == NULL || size == 0) + return NULL; + + result = g_malloc(3 * size); + + for (i = 0; i < size; i++) { + result[i * 3 + 0] = hex[(buffer[i] >> 4) & 0xf]; + result[i * 3 + 1] = hex[(buffer[i] >> 0) & 0xf]; + result[i * 3 + 2] = i == size - 1 ? '\0' : ':'; + } + + return result; +} diff --git a/src/core/misc.h b/src/core/misc.h index c095e131..00637da0 100644 --- a/src/core/misc.h +++ b/src/core/misc.h @@ -109,4 +109,8 @@ int find_substr(const char *list, const char *item); /* split `str' into `len' sized substrings */ char **strsplit_len(const char *str, int len, gboolean onspace); +/* Convert a given buffer to a printable, colon-delimited, hex string and + * return a pointer to the newly allocated buffer */ +char *binary_to_hex(unsigned char *buffer, size_t size); + #endif From 2be7289085d6969e6774ce3909f0224b1d689f93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 13:46:58 +0200 Subject: [PATCH 03/19] Rename SSL to TLS. This patch changes the internal name of SSL to TLS. We also add -tls_* options to /CONNECT and /SERVER, but make sure that the -ssl_* versions of the commands continue to work like before. --- NEWS | 3 + docs/help/in/connect.in | 33 +++++---- docs/help/in/server.in | 64 ++++++++--------- src/core/chat-commands.c | 44 ++++++------ src/core/network-openssl.c | 17 ++--- src/core/server-connect-rec.h | 16 ++--- src/core/server-setup-rec.h | 16 ++--- src/core/servers-reconnect.c | 14 ++-- src/core/servers-setup.c | 114 +++++++++++++++++++----------- src/core/servers.c | 28 ++++---- src/core/session.c | 17 +++-- src/fe-common/core/fe-server.c | 60 ++++++++++------ src/fe-common/irc/fe-irc-server.c | 30 ++++---- src/irc/core/irc-servers.c | 4 +- src/perl/perl-common.c | 3 +- 15 files changed, 255 insertions(+), 208 deletions(-) diff --git a/NEWS b/NEWS index ca6ecce3..8ecccc57 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ v0.8.21-head 2016-xx-xx The Irssi team tag/* and * to ignore whole networks or everything. + /hilight got a -matchcase flag to hilight case sensitively (#421). + Always build irssi with TLS support. + + Rename SSL to TLS in the code and add -tls_* versions of the -ssl_* + options to /CONNECT and /SERVER, but make sure the -ssl_* options continue + to work. - IP addresses are no longer stored when resolve_reverse_lookup is used. - /names and $[...] now uses utf8 string operations (#40, #411). diff --git a/docs/help/in/connect.in b/docs/help/in/connect.in index df50d1b9..a0d793d2 100644 --- a/docs/help/in/connect.in +++ b/docs/help/in/connect.in @@ -5,23 +5,22 @@ %9Parameters:%9 - -4: Connects using IPv4. - -6: Connects using IPv6. - -ssl: Connects using SSL encryption. - -ssl_cert: The SSL client certificate file. - -ssl_pkey: The SSL client private key, if not included in the - certificate file. - -ssl_pass: The password for the SSL client private key or certificate. - -ssl_verify: Verifies the SSL certificate of the server. - -ssl_cafile: The file with the list of CA certificates. - -ssl_capath: The directory which contains the CA certificates. - -ssl_ciphers: SSL cipher suite preference lists. - -noproxy: Ignores the global proxy configuration. - -network: The network this connection belongs to. - -host: The hostname you would like to connect from. - -rawlog: Immediately open rawlog after connecting. - -!: Doesn't autojoin channels. - -noautosendcmd: Doesn't execute autosendcmd. + -4: Connects using IPv4. + -6: Connects using IPv6. + -tls: Connects using TLS encryption. + -tls_cert: The TLS client certificate file. + -tls_pkey: The TLS client private key, if not included in the certificate file. + -tls_pass: The password for the TLS client private key or certificate. + -tls_verify: Verifies the TLS certificate of the server. + -tls_cafile: The file with the list of CA certificates. + -tls_capath: The directory which contains the CA certificates. + -tls_ciphers: TLS cipher suite preference lists. + -noproxy: Ignores the global proxy configuration. + -network: The network this connection belongs to. + -host: The hostname you would like to connect from. + -rawlog: Immediately open rawlog after connecting. + -!: Doesn't autojoin channels. + -noautosendcmd: Doesn't execute autosendcmd. A network or server to connect to; you can optionally specify a custom port, password and nickname. diff --git a/docs/help/in/server.in b/docs/help/in/server.in index 68a62e2d..ee1a30e1 100644 --- a/docs/help/in/server.in +++ b/docs/help/in/server.in @@ -5,45 +5,45 @@ %9Parameters:%9 - LIST: Displays the list of servers you are connected to. - CONNECT: Connects to the given server. - ADD: Adds a server to your configuration. - MODIFY: Modifies a server in your configuration. - REMOVE: Removes a server from your configuration. - PURGE: Purges the commands queued to be sent to the server. + LIST: Displays the list of servers you are connected to. + CONNECT: Connects to the given server. + ADD: Adds a server to your configuration. + MODIFY: Modifies a server in your configuration. + REMOVE: Removes a server from your configuration. + PURGE: Purges the commands queued to be sent to the server. - -!: Doesn't autojoin the channels. - -4: Connects using IPv4. - -6: Connects using IPv6. - -ssl: Connects using SSL encryption. - -ssl_cert: The SSL client certificate file. - -ssl_pkey: The SSL client private key, if not included in the - certificate file. - -ssl_pass: The password for the SSL client private key or certificate. - -ssl_verify: Verifies the SSL certificate of the server. - -ssl_cafile: The file with the list of CA certificates. - -ssl_capath: The directory which contains the CA certificates. - -ssl_ciphers: SSL cipher suite preference lists. - -auto: Automatically connects to the server on startup. - -noauto: Doesn't connect to the server on startup. - -network: The network the server belongs to. - -host: The hostname you would like to connect from. - -cmdspeed: Specifies the minimum amount of time, expressed in - milliseconds, that the client must wait before sending - additional commands to the server. - -cmdmax: Specifies the maximum number of commands to perform - before starting the internal flood protection. - -port: Specifies the port to connect to the server. - -noproxy: Ignores the global proxy configuration. - -rawlog: Immediately open rawlog after connecting. - -noautosendcmd: Doesn't execute autosendcmd. + -!: Doesn't autojoin the channels. + -4: Connects using IPv4. + -6: Connects using IPv6. + -tls: Connects using TLS encryption. + -tls_cert: The TLS client certificate file. + -tls_pkey: The TLS client private key, if not included in the + certificate file. + -tls_pass: The password for the TLS client private key or certificate. + -tls_verify: Verifies the TLS certificate of the server. + -tls_cafile: The file with the list of CA certificates. + -tls_capath: The directory which contains the CA certificates. + -tls_ciphers: TLS cipher suite preference lists. + -auto: Automatically connects to the server on startup. + -noauto: Doesn't connect to the server on startup. + -network: The network the server belongs to. + -host: The hostname you would like to connect from. + -cmdspeed: Specifies the minimum amount of time, expressed in + milliseconds, that the client must wait before sending + additional commands to the server. + -cmdmax: Specifies the maximum number of commands to perform + before starting the internal flood protection. + -port: Specifies the port to connect to the server. + -noproxy: Ignores the global proxy configuration. + -rawlog: Immediately open rawlog after connecting. + -noautosendcmd: Doesn't execute autosendcmd. The server, port and network to add, modify or remove; if no argument is given, the list of servers you are connected to will be returned. %9Description:%9 - Displays, adds, modifies or removes the network configuration of IRC + Displays, adds, modifies or removes the network configuration of IRC servers. When using the ADD parameter on a server that already exists, the diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index a9404fa3..db60e46f 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -99,27 +99,27 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, else if (g_hash_table_lookup(optlist, "4") != NULL) conn->family = AF_INET; - if (g_hash_table_lookup(optlist, "ssl") != NULL) - conn->use_ssl = TRUE; - if ((tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL) - conn->ssl_cert = g_strdup(tmp); - if ((tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL) - conn->ssl_pkey = g_strdup(tmp); - if ((tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL) - conn->ssl_pass = g_strdup(tmp); - if (g_hash_table_lookup(optlist, "ssl_verify") != NULL) - conn->ssl_verify = TRUE; - if ((tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL) - conn->ssl_cafile = g_strdup(tmp); - if ((tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL) - conn->ssl_capath = g_strdup(tmp); - if ((tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL) - conn->ssl_ciphers = g_strdup(tmp); - if ((conn->ssl_capath != NULL && conn->ssl_capath[0] != '\0') - || (conn->ssl_cafile != NULL && conn->ssl_cafile[0] != '\0')) - conn->ssl_verify = TRUE; - if ((conn->ssl_cert != NULL && conn->ssl_cert[0] != '\0') || conn->ssl_verify) - conn->use_ssl = TRUE; + if (g_hash_table_lookup(optlist, "tls") != NULL || g_hash_table_lookup(optlist, "ssl") != NULL) + conn->use_tls = TRUE; + if ((tmp = g_hash_table_lookup(optlist, "tls_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cert")) != NULL) + conn->tls_cert = g_strdup(tmp); + if ((tmp = g_hash_table_lookup(optlist, "tls_pkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pkey")) != NULL) + conn->tls_pkey = g_strdup(tmp); + if ((tmp = g_hash_table_lookup(optlist, "tls_pass")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pass")) != NULL) + conn->tls_pass = g_strdup(tmp); + if (g_hash_table_lookup(optlist, "tls_verify") != NULL || g_hash_table_lookup(optlist, "ssl_verify") != NULL) + conn->tls_verify = TRUE; + if ((tmp = g_hash_table_lookup(optlist, "tls_cafile")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_cafile")) != NULL) + conn->tls_cafile = g_strdup(tmp); + if ((tmp = g_hash_table_lookup(optlist, "tls_capath")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_capath")) != NULL) + conn->tls_capath = g_strdup(tmp); + if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL) + conn->tls_ciphers = g_strdup(tmp); + if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0') + || (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0')) + conn->tls_verify = TRUE; + if ((conn->tls_cert != NULL && conn->tls_cert[0] != '\0') || conn->tls_verify) + conn->use_tls = TRUE; if (g_hash_table_lookup(optlist, "!") != NULL) conn->no_autojoin_channels = TRUE; @@ -494,7 +494,7 @@ void chat_commands_init(void) signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server); signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg); - command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +host noproxy -rawlog noautosendcmd"); + command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +host noproxy -rawlog noautosendcmd"); command_set_options("msg", "channel nick"); } diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 014f0a28..82fd65b0 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -455,13 +455,13 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_ SSL *ssl; SSL_CTX *ctx = NULL; - const char *mycert = server->connrec->ssl_cert; - const char *mypkey = server->connrec->ssl_pkey; - const char *mypass = server->connrec->ssl_pass; - const char *cafile = server->connrec->ssl_cafile; - const char *capath = server->connrec->ssl_capath; - const char *ciphers = server->connrec->ssl_ciphers; - gboolean verify = server->connrec->ssl_verify; + const char *mycert = server->connrec->tls_cert; + const char *mypkey = server->connrec->tls_pkey; + const char *mypass = server->connrec->tls_pass; + const char *cafile = server->connrec->tls_cafile; + const char *capath = server->connrec->tls_capath; + const char *ciphers = server->connrec->tls_ciphers; + gboolean verify = server->connrec->tls_verify; g_return_val_if_fail(handle != NULL, NULL); @@ -480,7 +480,8 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_ SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_set_default_passwd_cb(ctx, get_pem_password_callback); SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)mypass); - if (ciphers && *ciphers) { + + if (ciphers != NULL && ciphers[0] != '\0') { if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) g_warning("No valid SSL cipher suite could be selected"); } diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index 80c5761b..35577fd4 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -23,12 +23,12 @@ char *nick; char *username; char *realname; -char *ssl_cert; -char *ssl_pkey; -char *ssl_pass; -char *ssl_cafile; -char *ssl_capath; -char *ssl_ciphers; +char *tls_cert; +char *tls_pkey; +char *tls_pass; +char *tls_cafile; +char *tls_capath; +char *tls_ciphers; GIOChannel *connect_handle; /* connect using this handle */ @@ -38,8 +38,8 @@ unsigned int reconnecting:1; /* we're trying to reconnect any connection */ unsigned int no_autojoin_channels:1; /* don't autojoin any channels */ unsigned int no_autosendcmd:1; /* don't execute autosendcmd */ unsigned int unix_socket:1; /* Connect using named unix socket */ -unsigned int use_ssl:1; /* this connection uses SSL */ -unsigned int ssl_verify:1; +unsigned int use_tls:1; /* this connection uses TLS */ +unsigned int tls_verify:1; unsigned int no_connect:1; /* don't connect() at all, it's done by plugin */ char *channels; char *away_reason; diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h index 2c9614c7..22876d4e 100644 --- a/src/core/server-setup-rec.h +++ b/src/core/server-setup-rec.h @@ -11,12 +11,12 @@ char *password; int sasl_mechanism; char *sasl_password; -char *ssl_cert; -char *ssl_pkey; -char *ssl_pass; -char *ssl_cafile; -char *ssl_capath; -char *ssl_ciphers; +char *tls_cert; +char *tls_pkey; +char *tls_pass; +char *tls_cafile; +char *tls_capath; +char *tls_ciphers; char *own_host; /* address to use when connecting this server */ IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */ @@ -28,7 +28,7 @@ unsigned int no_proxy:1; unsigned int last_failed:1; /* if last connection attempt failed */ unsigned int banned:1; /* if we're banned from this server */ unsigned int dns_error:1; /* DNS said the host doesn't exist */ -unsigned int use_ssl:1; /* this connection uses SSL */ -unsigned int ssl_verify:1; +unsigned int use_tls:1; /* this connection uses TLS */ +unsigned int tls_verify:1; GHashTable *module_data; diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index 58c9dd09..16ec1fac 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -192,13 +192,13 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) dest->no_autosendcmd = src->no_autosendcmd; dest->unix_socket = src->unix_socket; - dest->use_ssl = src->use_ssl; - dest->ssl_cert = g_strdup(src->ssl_cert); - dest->ssl_pkey = g_strdup(src->ssl_pkey); - dest->ssl_verify = src->ssl_verify; - dest->ssl_cafile = g_strdup(src->ssl_cafile); - dest->ssl_capath = g_strdup(src->ssl_capath); - dest->ssl_ciphers = g_strdup(src->ssl_ciphers); + dest->use_tls = src->use_tls; + dest->tls_cert = g_strdup(src->tls_cert); + dest->tls_pkey = g_strdup(src->tls_pkey); + dest->tls_verify = src->tls_verify; + dest->tls_cafile = g_strdup(src->tls_cafile); + dest->tls_capath = g_strdup(src->tls_capath); + dest->tls_ciphers = g_strdup(src->tls_ciphers); return dest; } diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index 0cecfece..01a36e1c 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -167,20 +167,20 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn, if (sserver->port > 0 && conn->port <= 0) conn->port = sserver->port; - conn->use_ssl = sserver->use_ssl; - if (conn->ssl_cert == NULL && sserver->ssl_cert != NULL && sserver->ssl_cert[0] != '\0') - conn->ssl_cert = g_strdup(sserver->ssl_cert); - if (conn->ssl_pkey == NULL && sserver->ssl_pkey != NULL && sserver->ssl_pkey[0] != '\0') - conn->ssl_pkey = g_strdup(sserver->ssl_pkey); - if (conn->ssl_pass == NULL && sserver->ssl_pass != NULL && sserver->ssl_pass[0] != '\0') - conn->ssl_pass = g_strdup(sserver->ssl_pass); - conn->ssl_verify = sserver->ssl_verify; - if (conn->ssl_cafile == NULL && sserver->ssl_cafile != NULL && sserver->ssl_cafile[0] != '\0') - conn->ssl_cafile = g_strdup(sserver->ssl_cafile); - if (conn->ssl_capath == NULL && sserver->ssl_capath != NULL && sserver->ssl_capath[0] != '\0') - conn->ssl_capath = g_strdup(sserver->ssl_capath); - if (conn->ssl_ciphers == NULL && sserver->ssl_ciphers != NULL && sserver->ssl_ciphers[0] != '\0') - conn->ssl_ciphers = g_strdup(sserver->ssl_ciphers); + conn->use_tls = sserver->use_tls; + if (conn->tls_cert == NULL && sserver->tls_cert != NULL && sserver->tls_cert[0] != '\0') + conn->tls_cert = g_strdup(sserver->tls_cert); + if (conn->tls_pkey == NULL && sserver->tls_pkey != NULL && sserver->tls_pkey[0] != '\0') + conn->tls_pkey = g_strdup(sserver->tls_pkey); + if (conn->tls_pass == NULL && sserver->tls_pass != NULL && sserver->tls_pass[0] != '\0') + conn->tls_pass = g_strdup(sserver->tls_pass); + conn->tls_verify = sserver->tls_verify; + if (conn->tls_cafile == NULL && sserver->tls_cafile != NULL && sserver->tls_cafile[0] != '\0') + conn->tls_cafile = g_strdup(sserver->tls_cafile); + if (conn->tls_capath == NULL && sserver->tls_capath != NULL && sserver->tls_capath[0] != '\0') + conn->tls_capath = g_strdup(sserver->tls_capath); + if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0') + conn->tls_ciphers = g_strdup(sserver->tls_ciphers); server_setup_fill_reconn(conn, sserver); @@ -362,9 +362,10 @@ SERVER_SETUP_REC *server_setup_find(const char *address, int port, static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) { SERVER_SETUP_REC *rec; - CHATNET_REC *chatnetrec; + CHATNET_REC *chatnetrec; char *server, *chatnet, *family; int port; + char *value = NULL; g_return_val_if_fail(node != NULL, NULL); @@ -390,7 +391,7 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) chatnet_create(chatnetrec); } - family = config_node_get_str(node, "family", ""); + family = config_node_get_str(node, "family", ""); rec = CHAT_PROTOCOL(chatnetrec)->create_server_setup(); rec->type = module_get_uniq_id("SERVER SETUP", 0); @@ -400,18 +401,45 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) (g_ascii_strcasecmp(family, "inet") == 0 ? AF_INET : 0); rec->address = g_strdup(server); rec->password = g_strdup(config_node_get_str(node, "password", NULL)); - rec->use_ssl = config_node_get_bool(node, "use_ssl", FALSE); - rec->ssl_cert = g_strdup(config_node_get_str(node, "ssl_cert", NULL)); - rec->ssl_pkey = g_strdup(config_node_get_str(node, "ssl_pkey", NULL)); - rec->ssl_pass = g_strdup(config_node_get_str(node, "ssl_pass", NULL)); - rec->ssl_verify = config_node_get_bool(node, "ssl_verify", FALSE); - rec->ssl_cafile = g_strdup(config_node_get_str(node, "ssl_cafile", NULL)); - rec->ssl_capath = g_strdup(config_node_get_str(node, "ssl_capath", NULL)); - rec->ssl_ciphers = g_strdup(config_node_get_str(node, "ssl_ciphers", NULL)); - if (rec->ssl_cafile || rec->ssl_capath) - rec->ssl_verify = TRUE; - if (rec->ssl_cert != NULL || rec->ssl_verify) - rec->use_ssl = TRUE; + + rec->use_tls = config_node_get_bool(node, "use_tls", FALSE) || config_node_get_bool(node, "use_ssl", FALSE); + rec->tls_verify = config_node_get_bool(node, "tls_verify", FALSE) || config_node_get_bool(node, "ssl_verify", FALSE); + + value = config_node_get_str(node, "tls_cert", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_cert", NULL); + rec->tls_cert = g_strdup(value); + + value = config_node_get_str(node, "tls_pkey", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_pkey", NULL); + rec->tls_pkey = g_strdup(value); + + value = config_node_get_str(node, "tls_pass", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_pass", NULL); + rec->tls_pass = g_strdup(value); + + value = config_node_get_str(node, "tls_cafile", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_cafile", NULL); + rec->tls_cafile = g_strdup(value); + + value = config_node_get_str(node, "tls_capath", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_capath", NULL); + rec->tls_capath = g_strdup(value); + + value = config_node_get_str(node, "tls_ciphers", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_ciphers", NULL); + rec->tls_ciphers = g_strdup(value); + + if (rec->tls_cafile || rec->tls_capath) + rec->tls_verify = TRUE; + if (rec->tls_cert != NULL || rec->tls_verify) + rec->use_tls = TRUE; + rec->port = port; rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE); rec->no_proxy = config_node_get_bool(node, "no_proxy", FALSE); @@ -463,14 +491,16 @@ static void server_setup_save(SERVER_SETUP_REC *rec) iconfig_node_set_int(node, "port", rec->port); iconfig_node_set_str(node, "password", rec->password); - iconfig_node_set_bool(node, "use_ssl", rec->use_ssl); - iconfig_node_set_str(node, "ssl_cert", rec->ssl_cert); - iconfig_node_set_str(node, "ssl_pkey", rec->ssl_pkey); - iconfig_node_set_str(node, "ssl_pass", rec->ssl_pass); - iconfig_node_set_bool(node, "ssl_verify", rec->ssl_verify); - iconfig_node_set_str(node, "ssl_cafile", rec->ssl_cafile); - iconfig_node_set_str(node, "ssl_capath", rec->ssl_capath); - iconfig_node_set_str(node, "ssl_ciphers", rec->ssl_ciphers); + + iconfig_node_set_bool(node, "use_tls", rec->use_tls); + iconfig_node_set_str(node, "tls_cert", rec->tls_cert); + iconfig_node_set_str(node, "tls_pkey", rec->tls_pkey); + iconfig_node_set_str(node, "tls_pass", rec->tls_pass); + iconfig_node_set_bool(node, "tls_verify", rec->tls_verify); + iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile); + iconfig_node_set_str(node, "tls_capath", rec->tls_capath); + iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers); + iconfig_node_set_str(node, "own_host", rec->own_host); iconfig_node_set_str(node, "family", @@ -514,12 +544,12 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec) g_free_not_null(rec->own_ip6); g_free_not_null(rec->chatnet); g_free_not_null(rec->password); - g_free_not_null(rec->ssl_cert); - g_free_not_null(rec->ssl_pkey); - g_free_not_null(rec->ssl_pass); - g_free_not_null(rec->ssl_cafile); - g_free_not_null(rec->ssl_capath); - g_free_not_null(rec->ssl_ciphers); + g_free_not_null(rec->tls_cert); + g_free_not_null(rec->tls_pkey); + g_free_not_null(rec->tls_pass); + g_free_not_null(rec->tls_cafile); + g_free_not_null(rec->tls_capath); + g_free_not_null(rec->tls_ciphers); g_free(rec->address); g_free(rec); } diff --git a/src/core/servers.c b/src/core/servers.c index dfcbcde0..2a14d510 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -219,7 +219,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, own_ip = IPADDR_IS_V6(ip) ? server->connrec->own_ip6 : server->connrec->own_ip4; port = server->connrec->proxy != NULL ? server->connrec->proxy_port : server->connrec->port; - handle = server->connrec->use_ssl ? + handle = server->connrec->use_tls ? net_connect_ip_ssl(ip, port, own_ip, server) : net_connect_ip(ip, port, own_ip); } else { handle = net_connect_unix(unix_socket); @@ -237,7 +237,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, } server->no_reconnect = TRUE; } - if (server->connrec->use_ssl && errno == ENOSYS) + if (server->connrec->use_tls && errno == ENOSYS) server->no_reconnect = TRUE; server->connection_lost = TRUE; @@ -245,7 +245,7 @@ static void server_real_connect(SERVER_REC *server, IPADDR *ip, g_free(errmsg2); } else { server->handle = net_sendbuffer_create(handle, 0); - if (server->connrec->use_ssl) + if (server->connrec->use_tls) server_connect_callback_init_ssl(server, handle); else server->connect_tag = @@ -622,22 +622,22 @@ void server_connect_unref(SERVER_CONNECT_REC *conn) g_free_not_null(conn->own_ip4); g_free_not_null(conn->own_ip6); - g_free_not_null(conn->password); - g_free_not_null(conn->nick); - g_free_not_null(conn->username); + g_free_not_null(conn->password); + g_free_not_null(conn->nick); + g_free_not_null(conn->username); g_free_not_null(conn->realname); - g_free_not_null(conn->ssl_cert); - g_free_not_null(conn->ssl_pkey); - g_free_not_null(conn->ssl_pass); - g_free_not_null(conn->ssl_cafile); - g_free_not_null(conn->ssl_capath); - g_free_not_null(conn->ssl_ciphers); + g_free_not_null(conn->tls_cert); + g_free_not_null(conn->tls_pkey); + g_free_not_null(conn->tls_pass); + g_free_not_null(conn->tls_cafile); + g_free_not_null(conn->tls_capath); + g_free_not_null(conn->tls_ciphers); g_free_not_null(conn->channels); - g_free_not_null(conn->away_reason); + g_free_not_null(conn->away_reason); - conn->type = 0; + conn->type = 0; g_free(conn); } diff --git a/src/core/session.c b/src/core/session.c index 17d80076..5b3303bb 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -150,8 +150,7 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config, node = config_node_section(config, node, NULL, NODE_TYPE_BLOCK); - config_node_set_str(config, node, "chat_type", - chat_protocol_find_id(server->chat_type)->name); + config_node_set_str(config, node, "chat_type", chat_protocol_find_id(server->chat_type)->name); config_node_set_str(config, node, "address", server->connrec->address); config_node_set_int(config, node, "port", server->connrec->port); config_node_set_str(config, node, "chatnet", server->connrec->chatnet); @@ -159,13 +158,13 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config, config_node_set_str(config, node, "nick", server->nick); config_node_set_str(config, node, "version", server->version); - config_node_set_bool(config, node, "use_ssl", server->connrec->use_ssl); - config_node_set_str(config, node, "ssl_cert", server->connrec->ssl_cert); - config_node_set_str(config, node, "ssl_pkey", server->connrec->ssl_pkey); - config_node_set_bool(config, node, "ssl_verify", server->connrec->ssl_verify); - config_node_set_str(config, node, "ssl_cafile", server->connrec->ssl_cafile); - config_node_set_str(config, node, "ssl_capath", server->connrec->ssl_capath); - config_node_set_str(config, node, "ssl_ciphers", server->connrec->ssl_ciphers); + config_node_set_bool(config, node, "use_tls", server->connrec->use_tls); + config_node_set_str(config, node, "tls_cert", server->connrec->tls_cert); + config_node_set_str(config, node, "tls_pkey", server->connrec->tls_pkey); + config_node_set_bool(config, node, "tls_verify", server->connrec->tls_verify); + config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile); + config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath); + config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers); handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); config_node_set_int(config, node, "handle", handle); diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index 468cb707..b9522bc1 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -154,42 +154,55 @@ static void cmd_server_add_modify(const char *data, gboolean add) else if (g_hash_table_lookup(optlist, "4")) rec->family = AF_INET; - if (g_hash_table_lookup(optlist, "ssl")) - rec->use_ssl = TRUE; + if (g_hash_table_lookup(optlist, "tls") || g_hash_table_lookup(optlist, "ssl")) + rec->use_tls = TRUE; - value = g_hash_table_lookup(optlist, "ssl_cert"); + value = g_hash_table_lookup(optlist, "tls_cert"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_cert"); if (value != NULL && *value != '\0') - rec->ssl_cert = g_strdup(value); + rec->tls_cert = g_strdup(value); - value = g_hash_table_lookup(optlist, "ssl_pkey"); + value = g_hash_table_lookup(optlist, "tls_pkey"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pkey"); if (value != NULL && *value != '\0') - rec->ssl_pkey = g_strdup(value); + rec->tls_pkey = g_strdup(value); - value = g_hash_table_lookup(optlist, "ssl_pass"); + value = g_hash_table_lookup(optlist, "tls_pass"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pass"); if (value != NULL && *value != '\0') - rec->ssl_pass = g_strdup(value); + rec->tls_pass = g_strdup(value); - if (g_hash_table_lookup(optlist, "ssl_verify")) - rec->ssl_verify = TRUE; + if (g_hash_table_lookup(optlist, "tls_verify") || g_hash_table_lookup(optlist, "ssl_verify")) + rec->tls_verify = TRUE; - value = g_hash_table_lookup(optlist, "ssl_cafile"); + value = g_hash_table_lookup(optlist, "tls_cafile"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_cafile"); if (value != NULL && *value != '\0') - rec->ssl_cafile = g_strdup(value); + rec->tls_cafile = g_strdup(value); - value = g_hash_table_lookup(optlist, "ssl_capath"); + value = g_hash_table_lookup(optlist, "tls_capath"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_capath"); if (value != NULL && *value != '\0') - rec->ssl_capath = g_strdup(value); + rec->tls_capath = g_strdup(value); - value = g_hash_table_lookup(optlist, "ssl_ciphers"); + value = g_hash_table_lookup(optlist, "tls_ciphers"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_ciphers"); if (value != NULL && *value != '\0') - rec->ssl_ciphers = g_strdup(value); + rec->tls_ciphers = g_strdup(value); - if ((rec->ssl_cafile != NULL && rec->ssl_cafile[0] != '\0') - || (rec->ssl_capath != NULL && rec->ssl_capath[0] != '\0')) - rec->ssl_verify = TRUE; - if ((rec->ssl_cert != NULL && rec->ssl_cert[0] != '\0') || rec->ssl_verify == TRUE) - rec->use_ssl = TRUE; + if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0') + || (rec->tls_capath != NULL && rec->tls_capath[0] != '\0')) + rec->tls_verify = TRUE; + + if ((rec->tls_cert != NULL && rec->tls_cert[0] != '\0') || rec->tls_verify == TRUE) + rec->use_tls = TRUE; if (g_hash_table_lookup(optlist, "auto")) rec->autoconnect = TRUE; if (g_hash_table_lookup(optlist, "noauto")) rec->autoconnect = FALSE; @@ -409,8 +422,9 @@ void fe_server_init(void) command_bind("server remove", NULL, (SIGNAL_FUNC) cmd_server_remove); command_bind_first("server", NULL, (SIGNAL_FUNC) server_command); command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command); - command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); - command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); + + command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); + command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting); diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c index 2e22d6f2..36ed2bdc 100644 --- a/src/fe-common/irc/fe-irc-server.c +++ b/src/fe-common/irc/fe-irc-server.c @@ -108,23 +108,23 @@ static void cmd_server_list(const char *data) g_string_append(str, "autoconnect, "); if (rec->no_proxy) g_string_append(str, "noproxy, "); - if (rec->use_ssl) { - g_string_append(str, "ssl, "); - if (rec->ssl_cert) { - g_string_append_printf(str, "ssl_cert: %s, ", rec->ssl_cert); - if (rec->ssl_pkey) - g_string_append_printf(str, "ssl_pkey: %s, ", rec->ssl_pkey); - if (rec->ssl_pass) + if (rec->use_tls) { + g_string_append(str, "tls, "); + if (rec->tls_cert) { + g_string_append_printf(str, "tls_cert: %s, ", rec->tls_cert); + if (rec->tls_pkey) + g_string_append_printf(str, "tls_pkey: %s, ", rec->tls_pkey); + if (rec->tls_pass) g_string_append_printf(str, "(pass), "); } - if (rec->ssl_verify) - g_string_append(str, "ssl_verify, "); - if (rec->ssl_cafile) - g_string_append_printf(str, "ssl_cafile: %s, ", rec->ssl_cafile); - if (rec->ssl_capath) - g_string_append_printf(str, "ssl_capath: %s, ", rec->ssl_capath); - if (rec->ssl_ciphers) - g_string_append_printf(str, "ssl_ciphers: %s, ", rec->ssl_ciphers); + if (rec->tls_verify) + g_string_append(str, "tls_verify, "); + if (rec->tls_cafile) + g_string_append_printf(str, "tls_cafile: %s, ", rec->tls_cafile); + if (rec->tls_capath) + g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath); + if (rec->tls_ciphers) + g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers); } if (rec->max_cmds_at_once > 0) diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 79aeb227..3117e345 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -310,7 +310,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn) if (server->connrec->port <= 0) { server->connrec->port = - server->connrec->use_ssl ? 6697 : 6667; + server->connrec->use_tls ? 6697 : 6667; } server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ? @@ -328,7 +328,7 @@ SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn) ircconn->max_whois : DEFAULT_MAX_WHOIS; server->max_msgs_in_cmd = ircconn->max_msgs > 0 ? ircconn->max_msgs : DEFAULT_MAX_MSGS; - server->connrec->use_ssl = conn->use_ssl; + server->connrec->use_tls = conn->use_tls; modes_server_init(server); diff --git a/src/perl/perl-common.c b/src/perl/perl-common.c index b641867f..1d08319f 100644 --- a/src/perl/perl-common.c +++ b/src/perl/perl-common.c @@ -301,7 +301,8 @@ void perl_connect_fill_hash(HV *hv, SERVER_CONNECT_REC *conn) (void) hv_store(hv, "no_autojoin_channels", 20, newSViv(conn->no_autojoin_channels), 0); (void) hv_store(hv, "no_autosendcmd", 14, newSViv(conn->no_autosendcmd), 0); (void) hv_store(hv, "unix_socket", 11, newSViv(conn->unix_socket), 0); - (void) hv_store(hv, "use_ssl", 7, newSViv(conn->use_ssl), 0); + (void) hv_store(hv, "use_ssl", 7, newSViv(conn->use_tls), 0); + (void) hv_store(hv, "use_tls", 7, newSViv(conn->use_tls), 0); (void) hv_store(hv, "no_connect", 10, newSViv(conn->no_connect), 0); } From 6a286a4eb284cda4dce9ed97fec2aca643a139b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 13:48:43 +0200 Subject: [PATCH 04/19] Use TLS by default for Freenode, IRC6, OFTC, EsperNet, EFnet, and Rizon. --- NEWS | 2 ++ irssi.conf | 12 ++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 8ecccc57..f94ede27 100644 --- a/NEWS +++ b/NEWS @@ -21,6 +21,8 @@ v0.8.21-head 2016-xx-xx The Irssi team + Rename SSL to TLS in the code and add -tls_* versions of the -ssl_* options to /CONNECT and /SERVER, but make sure the -ssl_* options continue to work. + + Use TLS for Freenode, EFnet, EsperNet, OFTC, Rizon, and IRC6 in the default + configuration. - IP addresses are no longer stored when resolve_reverse_lookup is used. - /names and $[...] now uses utf8 string operations (#40, #411). diff --git a/irssi.conf b/irssi.conf index ba159f34..0e486807 100644 --- a/irssi.conf +++ b/irssi.conf @@ -1,16 +1,16 @@ servers = ( { address = "irc.dal.net"; chatnet = "DALnet"; port = "6667"; }, - { address = "irc.efnet.org"; chatnet = "EFNet"; port = "6667"; }, - { address = "irc.esper.net"; chatnet = "EsperNet"; port = "6667"; }, - { address = "chat.freenode.net"; chatnet = "Freenode"; port = "6667"; }, + { address = "ssl.efnet.org"; chatnet = "EFNet"; port = "9999"; use_tls = "yes"; }, + { address = "irc.esper.net"; chatnet = "EsperNet"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; }, + { address = "chat.freenode.net"; chatnet = "Freenode"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; }, { address = "irc.gamesurge.net"; chatnet = "GameSurge"; port = "6667"; }, - { address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; }, + { address = "eu.irc6.net"; chatnet = "IRCnet"; port = "6667"; use_tls = "yes"; }, { address = "open.ircnet.net"; chatnet = "IRCnet"; port = "6667"; }, { address = "irc.ircsource.net"; chatnet = "IRCSource"; port = "6667"; }, { address = "irc.netfuze.net"; chatnet = "NetFuze"; port = "6667"; }, - { address = "irc.oftc.net"; chatnet = "OFTC"; port = "6667"; }, + { address = "irc.oftc.net"; chatnet = "OFTC"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; }, { address = "irc.quakenet.org"; chatnet = "QuakeNet"; port = "6667"; }, - { address = "irc.rizon.net"; chatnet = "Rizon"; port = "6667"; }, + { address = "irc.rizon.net"; chatnet = "Rizon"; port = "6697"; use_tls = "yes"; tls_verify = "yes"; }, { address = "silc.silcnet.org"; chatnet = "SILC"; port = "706"; }, { address = "irc.undernet.org"; chatnet = "Undernet"; port = "6667"; } ); From 99d017720dd162122754bfbedc96744f4af8ce6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 14:07:22 +0200 Subject: [PATCH 05/19] Add TLS_REC. This patch adds the TLS_REC structure. This structure is used to emit information about the TLS handshake from the core of irssi to the front-end layers such that we can display connection information to the user. --- docs/signals.txt | 1 + src/core/Makefile.am | 2 + src/core/tls.c | 219 ++++++++++++++++++++++++++++++++++++++++ src/core/tls.h | 95 +++++++++++++++++ src/perl/get-signals.pl | 1 + 5 files changed, 318 insertions(+) create mode 100644 src/core/tls.c create mode 100644 src/core/tls.h diff --git a/docs/signals.txt b/docs/signals.txt index 5e03d901..47db3575 100644 --- a/docs/signals.txt +++ b/docs/signals.txt @@ -59,6 +59,7 @@ network-openssl.c: "tlsa available", SERVER_REC "tlsa verification success", SERVER_REC "tlsa verification failed", SERVER_REC + "tls handshake finished", SERVER_REC, TLS_REC nicklist.c: "nicklist new", CHANNEL_REC, NICK_REC diff --git a/src/core/Makefile.am b/src/core/Makefile.am index af323234..10bd035a 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -46,6 +46,7 @@ libcore_a_SOURCES = \ special-vars.c \ utf8.c \ wcwidth.c \ + tls.c \ write-buffer.c structure_headers = \ @@ -97,5 +98,6 @@ pkginc_core_HEADERS = \ special-vars.h \ utf8.h \ window-item-def.h \ + tls.h \ write-buffer.h \ $(structure_headers) diff --git a/src/core/tls.c b/src/core/tls.c new file mode 100644 index 00000000..8aad0c89 --- /dev/null +++ b/src/core/tls.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2015 Alexander Færøy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#include "module.h" + +#include "tls.h" + +TLS_REC *tls_create_rec() +{ + TLS_REC *rec = g_new0(TLS_REC, 1); + g_return_val_if_fail(rec != NULL, NULL); + + return rec; +} + +void tls_rec_free(TLS_REC *tls_rec) +{ + if (tls_rec == NULL) + return; + + g_free_and_null(tls_rec->protocol_version); + g_free_and_null(tls_rec->cipher); + g_free_and_null(tls_rec->public_key_algorithm); + g_free_and_null(tls_rec->public_key_fingerprint); + g_free_and_null(tls_rec->public_key_fingerprint_algorithm); + g_free_and_null(tls_rec->certificate_fingerprint); + g_free_and_null(tls_rec->certificate_fingerprint_algorithm); + g_free_and_null(tls_rec->not_after); + g_free_and_null(tls_rec->not_before); + +#ifdef SSL_get_server_tmp_key + g_free_and_null(tls_rec->ephemeral_key_algorithm); +#endif + + if (tls_rec->certs != NULL) { + g_slist_foreach(tls_rec->certs, (GFunc)tls_cert_rec_free, NULL); + g_slist_free(tls_rec->certs); + tls_rec->certs = NULL; + } + + g_free(tls_rec); +} + +void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->protocol_version = g_strdup(protocol_version); +} + +void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->cipher = g_strdup(cipher); +} + +void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->cipher_size = size; +} + +void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->public_key_algorithm = g_strdup(algorithm); +} + +void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->public_key_fingerprint = g_strdup(fingerprint); +} + +void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->public_key_fingerprint_algorithm = g_strdup(algorithm); +} + +void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size) +{ + g_return_if_fail(tls_rec != NULL); + tls_rec->public_key_size = size; +} + +void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->certificate_fingerprint = g_strdup(fingerprint); +} + +void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm) +{ + g_return_if_fail(tls_rec != NULL); + + tls_rec->certificate_fingerprint_algorithm = g_strdup(algorithm); +} + +void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after) +{ + g_return_if_fail(tls_rec != NULL); + tls_rec->not_after = g_strdup(not_after); +} + +void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before) +{ + g_return_if_fail(tls_rec != NULL); + tls_rec->not_before = g_strdup(not_before); +} + +#ifdef SSL_get_server_tmp_key +void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm) +{ + g_return_if_fail(tls_rec != NULL); + tls_rec->ephemeral_key_algorithm = g_strdup(algorithm); +} + +void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size) +{ + g_return_if_fail(tls_rec != NULL); + tls_rec->ephemeral_key_size = size; +} +#endif + +void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec) +{ + g_return_if_fail(tls_rec != NULL); + g_return_if_fail(tls_cert_rec != NULL); + + tls_rec->certs = g_slist_append(tls_rec->certs, tls_cert_rec); +} + +TLS_CERT_REC *tls_cert_create_rec() +{ + TLS_CERT_REC *rec = g_new0(TLS_CERT_REC, 1); + g_return_val_if_fail(rec != NULL, NULL); + + return rec; +} + +void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec) +{ + g_return_if_fail(tls_cert_rec != NULL); + g_return_if_fail(tls_cert_entry_rec != NULL); + + tls_cert_rec->subject = g_slist_append(tls_cert_rec->subject, tls_cert_entry_rec); +} + +void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec) +{ + g_return_if_fail(tls_cert_rec != NULL); + g_return_if_fail(tls_cert_entry_rec != NULL); + + tls_cert_rec->issuer = g_slist_append(tls_cert_rec->issuer, tls_cert_entry_rec); +} + +void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec) +{ + if (tls_cert_rec == NULL) + return; + + if (tls_cert_rec->subject != NULL) { + g_slist_foreach(tls_cert_rec->subject, (GFunc)tls_cert_entry_rec_free, NULL); + g_slist_free(tls_cert_rec->subject); + tls_cert_rec->subject = NULL; + } + + if (tls_cert_rec->issuer != NULL) { + g_slist_foreach(tls_cert_rec->issuer, (GFunc)tls_cert_entry_rec_free, NULL); + g_slist_free(tls_cert_rec->issuer); + tls_cert_rec->issuer = NULL; + } + + g_free(tls_cert_rec); +} + +TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value) +{ + TLS_CERT_ENTRY_REC *rec = g_new0(TLS_CERT_ENTRY_REC, 1); + g_return_val_if_fail(rec != NULL, NULL); + + rec->name = g_strdup(name); + rec->value = g_strdup(value); + + return rec; +} + +void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry) +{ + if (tls_cert_entry == NULL) + return; + + g_free_and_null(tls_cert_entry->name); + g_free_and_null(tls_cert_entry->value); + + g_free(tls_cert_entry); +} diff --git a/src/core/tls.h b/src/core/tls.h new file mode 100644 index 00000000..4991f7ed --- /dev/null +++ b/src/core/tls.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015 Alexander Færøy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#ifndef __TLS_H +#define __TLS_H + +#include + +#include + +typedef struct _TLS_REC TLS_REC; +typedef struct _TLS_CERT_REC TLS_CERT_REC; +typedef struct _TLS_CERT_ENTRY_REC TLS_CERT_ENTRY_REC; + +struct _TLS_REC { + char *protocol_version; + char *cipher; + size_t cipher_size; + + char *public_key_algorithm; + char *public_key_fingerprint; + char *public_key_fingerprint_algorithm; + size_t public_key_size; + + char *certificate_fingerprint; + char *certificate_fingerprint_algorithm; + + char *not_after; + char *not_before; + +#ifdef SSL_get_server_tmp_key + char *ephemeral_key_algorithm; + size_t ephemeral_key_size; +#endif + + GSList *certs; +}; + +struct _TLS_CERT_REC { + GSList *subject; + GSList *issuer; +}; + +struct _TLS_CERT_ENTRY_REC { + char *name; + char *value; +}; + +TLS_REC *tls_create_rec(); +void tls_rec_free(TLS_REC *tls_rec); + +void tls_rec_set_protocol_version(TLS_REC *tls_rec, const char *protocol_version); +void tls_rec_set_cipher(TLS_REC *tls_rec, const char *cipher); +void tls_rec_set_cipher_size(TLS_REC *tls_rec, size_t size); +void tls_rec_set_public_key_algorithm(TLS_REC *tls_rec, const char *algorithm); +void tls_rec_set_public_key_fingerprint(TLS_REC *tls_rec, const char *fingerprint); +void tls_rec_set_public_key_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm); +void tls_rec_set_public_key_size(TLS_REC *tls_rec, size_t size); +void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerprint); +void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm); +void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after); +void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before); + +#ifdef SSL_get_server_tmp_key +void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm); +void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size); +#endif + +void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec); + +TLS_CERT_REC *tls_cert_create_rec(); +void tls_cert_rec_free(TLS_CERT_REC *tls_cert_rec); + +void tls_cert_rec_append_subject_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec); +void tls_cert_rec_append_issuer_entry(TLS_CERT_REC *tls_cert_rec, TLS_CERT_ENTRY_REC *tls_cert_entry_rec); + +TLS_CERT_ENTRY_REC *tls_cert_entry_create_rec(const char *name, const char *value); +void tls_cert_entry_rec_free(TLS_CERT_ENTRY_REC *tls_cert_entry); + +#endif diff --git a/src/perl/get-signals.pl b/src/perl/get-signals.pl index 529d35b1..806bcafa 100755 --- a/src/perl/get-signals.pl +++ b/src/perl/get-signals.pl @@ -37,6 +37,7 @@ while () { RAWLOG_REC => 'Irssi::Rawlog', IGNORE_REC => 'Irssi::Ignore', MODULE_REC => 'Irssi::Module', + TLS_REC => 'iobject', # irc BAN_REC => 'Irssi::Irc::Ban', From b630fd1703ef3a87060bb6d2acbc562b9c1c9081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 14:14:07 +0200 Subject: [PATCH 06/19] Populate and emit TLS_REC after TLS handshake have completed. --- src/core/network-openssl.c | 258 ++++++++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 7 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 82fd65b0..55fb1157 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -22,6 +22,8 @@ #include "network.h" #include "misc.h" #include "servers.h" +#include "signals.h" +#include "tls.h" #include #include @@ -201,9 +203,10 @@ static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname) return matched; } -static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server) +static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, int port, X509 *cert, SERVER_REC *server, TLS_REC *tls_rec) { long result; + #ifdef HAVE_DANE int dane_ret; struct val_daneparams daneparams; @@ -564,6 +567,197 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_ return gchan; } +static void set_cipher_info(TLS_REC *tls, SSL *ssl) +{ + g_return_if_fail(tls != NULL); + g_return_if_fail(ssl != NULL); + + tls_rec_set_protocol_version(tls, SSL_get_version(ssl)); + + tls_rec_set_cipher(tls, SSL_CIPHER_get_name(SSL_get_current_cipher(ssl))); + tls_rec_set_cipher_size(tls, SSL_get_cipher_bits(ssl, NULL)); +} + +static void set_pubkey_info(TLS_REC *tls, X509 *cert, unsigned char *cert_fingerprint, size_t cert_fingerprint_size, unsigned char *public_key_fingerprint, size_t public_key_fingerprint_size) +{ + g_return_if_fail(tls != NULL); + g_return_if_fail(cert != NULL); + + EVP_PKEY *pubkey = NULL; + char *cert_fingerprint_hex = NULL; + char *public_key_fingerprint_hex = NULL; + + BIO *bio = NULL; + char buffer[128]; + size_t length; + + pubkey = X509_get_pubkey(cert); + + cert_fingerprint_hex = binary_to_hex(cert_fingerprint, cert_fingerprint_size); + tls_rec_set_certificate_fingerprint(tls, cert_fingerprint_hex); + tls_rec_set_certificate_fingerprint_algorithm(tls, "SHA256"); + + // Show algorithm. + switch (EVP_PKEY_id(pubkey)) { + case EVP_PKEY_RSA: + tls_rec_set_public_key_algorithm(tls, "RSA"); + break; + + case EVP_PKEY_DSA: + tls_rec_set_public_key_algorithm(tls, "DSA"); + break; + + case EVP_PKEY_EC: + tls_rec_set_public_key_algorithm(tls, "EC"); + break; + + default: + tls_rec_set_public_key_algorithm(tls, "Unknown"); + break; + } + + public_key_fingerprint_hex = binary_to_hex(public_key_fingerprint, public_key_fingerprint_size); + tls_rec_set_public_key_fingerprint(tls, public_key_fingerprint_hex); + tls_rec_set_public_key_size(tls, EVP_PKEY_bits(pubkey)); + tls_rec_set_public_key_fingerprint_algorithm(tls, "SHA256"); + + // Read the NotBefore timestamp. + bio = BIO_new(BIO_s_mem()); + ASN1_TIME_print(bio, X509_get_notBefore(cert)); + length = BIO_read(bio, buffer, sizeof(buffer)); + buffer[length] = '\0'; + BIO_free(bio); + tls_rec_set_not_before(tls, buffer); + + // Read the NotAfter timestamp. + bio = BIO_new(BIO_s_mem()); + ASN1_TIME_print(bio, X509_get_notAfter(cert)); + length = BIO_read(bio, buffer, sizeof(buffer)); + buffer[length] = '\0'; + BIO_free(bio); + tls_rec_set_not_after(tls, buffer); + + g_free(cert_fingerprint_hex); + g_free(public_key_fingerprint_hex); + EVP_PKEY_free(pubkey); +} + +static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) +{ + g_return_if_fail(tls != NULL); + g_return_if_fail(ssl != NULL); + + STACK_OF(X509) *chain = NULL; + int i; + + chain = SSL_get_peer_cert_chain(ssl); + + if (chain == NULL) + return; + + for (i = 0; i < sk_X509_num(chain); i++) { + TLS_CERT_REC *cert_rec = NULL; + X509_NAME *name = NULL; + + int j; + int nid; + + char *key = NULL; + char *value = NULL; + + cert_rec = tls_cert_create_rec(); + + // Subject. + name = X509_get_subject_name(sk_X509_value(chain, i)); + + for (j = 0; j < X509_NAME_entry_count(name); j++) { + X509_NAME_ENTRY *entry = NULL; + TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL; + + entry = X509_NAME_get_entry(name, j); + + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); + key = (char *)OBJ_nid2sn(nid); + + if (key == NULL) + key = (char *)OBJ_nid2ln(nid); + + ASN1_STRING *d = X509_NAME_ENTRY_get_data(entry); + value = (char *)ASN1_STRING_data(d); + + tls_cert_entry_rec = tls_cert_entry_create_rec(key, value); + tls_cert_rec_append_subject_entry(cert_rec, tls_cert_entry_rec); + } + + // Issuer. + name = X509_get_issuer_name(sk_X509_value(chain, i)); + + for (j = 0; j < X509_NAME_entry_count(name); j++) { + X509_NAME_ENTRY *entry = NULL; + TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL; + + entry = X509_NAME_get_entry(name, j); + + nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); + key = (char *)OBJ_nid2sn(nid); + + if (key == NULL) + key = (char *)OBJ_nid2ln(nid); + + ASN1_STRING *d = X509_NAME_ENTRY_get_data(entry); + value = (char *)ASN1_STRING_data(d); + + tls_cert_entry_rec = tls_cert_entry_create_rec(key, value); + tls_cert_rec_append_issuer_entry(cert_rec, tls_cert_entry_rec); + } + + tls_rec_append_cert(tls, cert_rec); + } +} + +static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl) +{ + g_return_if_fail(tls != NULL); + g_return_if_fail(ssl != NULL); + +#ifdef SSL_get_server_tmp_key + // Show ephemeral key information. + EVP_PKEY *ephemeral_key = NULL; + + if (SSL_get_server_tmp_key(ssl, &ephemeral_key)) { + switch (EVP_PKEY_id(ephemeral_key)) { + case EVP_PKEY_DH: + tls_rec_set_ephemeral_key_algorithm(tls, "DH"); + tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); + break; + + case EVP_PKEY_EC: + { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(ephemeral_key); + int nid; + const char *cname; + nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); + EC_KEY_free(ec); + cname = OBJ_nid2sn(nid); + char *ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname); + + tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm); + tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); + + g_free_and_null(ephemeral_key_algorithm); + break; + + default: + tls_rec_set_ephemeral_key_algorithm(tls, "Unknown"); + tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); + break; + } + + EVP_PKEY_free(ephemeral_key); + } +#endif // SSL_get_server_tmp_key. +} + GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, SERVER_REC *server) { GIOChannel *handle, *ssl_handle; @@ -581,8 +775,17 @@ int irssi_ssl_handshake(GIOChannel *handle) { GIOSSLChannel *chan = (GIOSSLChannel *)handle; int ret, err; - X509 *cert; - const char *errstr; + const char *errstr = NULL; + X509 *cert = NULL; + X509_PUBKEY *pubkey = NULL; + int pubkey_size = 0; + unsigned char *pubkey_der = NULL; + unsigned char *pubkey_der_tmp = NULL; + unsigned char pubkey_fingerprint[EVP_MAX_MD_SIZE]; + unsigned int pubkey_fingerprint_size; + unsigned char cert_fingerprint[EVP_MAX_MD_SIZE]; + unsigned int cert_fingerprint_size; + TLS_REC *tls = NULL; ERR_clear_error(); ret = SSL_connect(chan->ssl); @@ -610,14 +813,55 @@ int irssi_ssl_handshake(GIOChannel *handle) } cert = SSL_get_peer_certificate(chan->ssl); + pubkey = X509_get_X509_PUBKEY(cert); + if (cert == NULL) { g_warning("SSL server supplied no certificate"); return -1; } - ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server); + + if (pubkey == NULL) { + g_warning("SSL server supplied no certificate public key"); + return -1; + } + + if (! X509_digest(cert, EVP_sha256(), cert_fingerprint, &cert_fingerprint_size)) { + g_warning("Unable to generate certificate fingerprint"); + X509_free(cert); + return -1; + } + + pubkey_size = i2d_X509_PUBKEY(pubkey, NULL); + pubkey_der = pubkey_der_tmp = g_new(unsigned char, pubkey_size); + i2d_X509_PUBKEY(pubkey, &pubkey_der_tmp); + + EVP_Digest(pubkey_der, pubkey_size, pubkey_fingerprint, &pubkey_fingerprint_size, EVP_sha256(), 0); + + tls = tls_create_rec(); + set_cipher_info(tls, chan->ssl); + set_pubkey_info(tls, cert, cert_fingerprint, cert_fingerprint_size, pubkey_fingerprint, pubkey_fingerprint_size); + set_peer_cert_chain_info(tls, chan->ssl); + set_server_temporary_key_info(tls, chan->ssl); + + ret = 1; + + do { + if (chan->verify) { + ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls); + + if (! ret) { + // irssi_ssl_verify emits a warning itself. + continue; + } + } + } while (0); + + // Emit the TLS rec. + signal_emit("tls handshake finished", 2, chan->server, tls); + + tls_rec_free(tls); X509_free(cert); + g_free(pubkey_der); + return ret ? 0 : -1; } - - - From 13f75d49e05b4e29104ef52ee1742564c7eed8df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 14:15:29 +0200 Subject: [PATCH 07/19] Simplify TLS verification error handling. --- src/core/network-openssl.c | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 55fb1157..8e12bd53 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -243,39 +243,7 @@ static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, i result = SSL_get_verify_result(ssl); if (result != X509_V_OK) { - unsigned char md[EVP_MAX_MD_SIZE]; - unsigned int n; - char *str; - - g_warning("Could not verify SSL servers certificate: %s", - X509_verify_cert_error_string(result)); - if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL) - g_warning(" Could not get subject-name from peer certificate"); - else { - g_warning(" Subject : %s", str); - free(str); - } - if ((str = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0)) == NULL) - g_warning(" Could not get issuer-name from peer certificate"); - else { - g_warning(" Issuer : %s", str); - free(str); - } - if (! X509_digest(cert, EVP_md5(), md, &n)) - g_warning(" Could not get fingerprint from peer certificate"); - else { - char hex[] = "0123456789ABCDEF"; - char fp[EVP_MAX_MD_SIZE*3]; - if (n < sizeof(fp)) { - unsigned int i; - for (i = 0; i < n; i++) { - fp[i*3+0] = hex[(md[i] >> 4) & 0xF]; - fp[i*3+1] = hex[(md[i] >> 0) & 0xF]; - fp[i*3+2] = i == n - 1 ? '\0' : ':'; - } - g_warning(" MD5 Fingerprint : %s", fp); - } - } + g_warning("Could not verify TLS servers certificate: %s", X509_verify_cert_error_string(result)); return FALSE; } else if (! irssi_ssl_verify_hostname(cert, hostname)){ return FALSE; From 1d101afe0d5e552f08bf87bcc242bcaf2db972f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 14:25:19 +0200 Subject: [PATCH 08/19] s/SSL/TLS/ for warning strings. --- src/core/network-openssl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 8e12bd53..c221624f 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -481,7 +481,7 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_ if (capath && *capath) scapath = convert_home(capath); if (! SSL_CTX_load_verify_locations(ctx, scafile, scapath)) { - g_warning("Could not load CA list for verifying SSL server certificate"); + g_warning("Could not load CA list for verifying TLS server certificate"); g_free(scafile); g_free(scapath); SSL_CTX_free(ctx); @@ -784,12 +784,12 @@ int irssi_ssl_handshake(GIOChannel *handle) pubkey = X509_get_X509_PUBKEY(cert); if (cert == NULL) { - g_warning("SSL server supplied no certificate"); + g_warning("TLS server supplied no certificate"); return -1; } if (pubkey == NULL) { - g_warning("SSL server supplied no certificate public key"); + g_warning("TLS server supplied no certificate public key"); return -1; } From c6c2e795376e4659810360deaa3dd28475d0f7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 14:33:25 +0200 Subject: [PATCH 09/19] Display TLS connection information when connected to a TLS enabled server. --- NEWS | 2 + src/fe-common/core/Makefile.am | 2 + src/fe-common/core/fe-common-core.c | 5 ++ src/fe-common/core/fe-tls.c | 82 +++++++++++++++++++++++++++++ src/fe-common/core/fe-tls.h | 25 +++++++++ src/fe-common/core/module-formats.c | 14 +++++ src/fe-common/core/module-formats.h | 15 +++++- 7 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/fe-common/core/fe-tls.c create mode 100644 src/fe-common/core/fe-tls.h diff --git a/NEWS b/NEWS index f94ede27..4dad1712 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,8 @@ v0.8.21-head 2016-xx-xx The Irssi team to work. + Use TLS for Freenode, EFnet, EsperNet, OFTC, Rizon, and IRC6 in the default configuration. + + Display TLS connection information upon connect. You can disable this by + setting tls_verbose_connect to FALSE. - IP addresses are no longer stored when resolve_reverse_lookup is used. - /names and $[...] now uses utf8 string operations (#40, #411). diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am index 63f91fa6..6efff411 100644 --- a/src/fe-common/core/Makefile.am +++ b/src/fe-common/core/Makefile.am @@ -24,6 +24,7 @@ libfe_common_core_a_SOURCES = \ fe-queries.c \ fe-server.c \ fe-settings.c \ + fe-tls.c \ formats.c \ hilight-text.c \ keyboard.c \ @@ -48,6 +49,7 @@ pkginc_fe_common_core_HEADERS = \ fe-exec.h \ fe-messages.h \ fe-queries.h \ + fe-tls.h \ formats.h \ hilight-text.h \ keyboard.h \ diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c index 1b2ab1e2..fd0e6cf0 100644 --- a/src/fe-common/core/fe-common-core.c +++ b/src/fe-common/core/fe-common-core.c @@ -88,6 +88,9 @@ void fe_server_deinit(void); void fe_settings_init(void); void fe_settings_deinit(void); +void fe_tls_init(void); +void fe_tls_deinit(void); + void window_commands_init(void); void window_commands_deinit(void); @@ -176,6 +179,7 @@ void fe_common_core_init(void) fe_modules_init(); fe_server_init(); fe_settings_init(); + fe_tls_init(); windows_init(); window_activity_init(); window_commands_init(); @@ -217,6 +221,7 @@ void fe_common_core_deinit(void) fe_modules_deinit(); fe_server_deinit(); fe_settings_deinit(); + fe_tls_deinit(); windows_deinit(); window_activity_deinit(); window_commands_deinit(); diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c new file mode 100644 index 00000000..ce9bddbf --- /dev/null +++ b/src/fe-common/core/fe-tls.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015 Alexander Færøy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#include "module.h" +#include "signals.h" +#include "settings.h" +#include "levels.h" +#include "tls.h" + +#include "module-formats.h" +#include "printtext.h" + +#include "fe-tls.h" + +static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls) +{ + if (! settings_get_bool("tls_verbose_connect")) + return; + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER); + + GSList *certs = NULL; + for (certs = tls->certs; certs != NULL; certs = certs->next) { + TLS_CERT_REC *tls_cert_rec = certs->data; + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT_HEADER); + + GSList *subject = NULL; + for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) { + TLS_CERT_ENTRY_REC *subject_data = subject->data; + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, subject_data->name, subject_data->value); + } + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER_HEADER); + + GSList *issuer = NULL; + for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) { + TLS_CERT_ENTRY_REC *issuer_data = issuer->data; + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, issuer_data->name, issuer_data->value); + } + } + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher); + +#ifdef SSL_get_server_tmp_key + if (tls->ephemeral_key_algorithm != NULL) + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm); + else + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE); +#endif + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_FINGERPRINT, tls->certificate_fingerprint, tls->certificate_fingerprint_algorithm); +} + +void fe_tls_init(void) +{ + settings_add_bool("lookandfeel", "tls_verbose_connect", TRUE); + + signal_add("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished); +} + +void fe_tls_deinit(void) +{ + signal_remove("tls handshake finished", (SIGNAL_FUNC)tls_handshake_finished); +} diff --git a/src/fe-common/core/fe-tls.h b/src/fe-common/core/fe-tls.h new file mode 100644 index 00000000..f0082477 --- /dev/null +++ b/src/fe-common/core/fe-tls.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Alexander Færøy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#ifndef __FE_TLS_H +#define __FE_TLS_H + +void fe_tls_init(void); +void fe_tls_deinit(void); + +#endif diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c index 5c07f14c..b4747d16 100644 --- a/src/fe-common/core/module-formats.c +++ b/src/fe-common/core/module-formats.c @@ -291,5 +291,19 @@ FORMAT_REC fecommon_core_formats[] = { { "completion_line", "%#$[10]0 $[!40]1 $2", 3, { 0, 0, 0 } }, { "completion_footer", "", 0 }, + /* ---- */ + { NULL, "TLS", 0 }, + + { "tls_ephemeral_key", "EDH Key: {hilight $0} bit {hilight $1}", 2, { 1, 0 } }, + { "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 }, + { "tls_pubkey", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } }, + { "tls_cert_header", "Certificate Chain:", 0 }, + { "tls_cert_subject_header", " Subject:", 0 }, + { "tls_cert_issuer_header", " Issuer:", 0 }, + { "tls_cert_named_entry", " $[-2]0: {hilight $1}", 2, { 0, 0 } }, + { "tls_pubkey_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } }, + { "tls_cert_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } }, + { "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } }, + { NULL, NULL, 0 } }; diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h index 2b45ff6b..4cdeb407 100644 --- a/src/fe-common/core/module-formats.h +++ b/src/fe-common/core/module-formats.h @@ -254,7 +254,20 @@ enum { TXT_COMPLETION_REMOVED, TXT_COMPLETION_HEADER, TXT_COMPLETION_LINE, - TXT_COMPLETION_FOOTER + TXT_COMPLETION_FOOTER, + + TLS_FILL_15, + + TXT_TLS_EPHEMERAL_KEY, + TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE, + TXT_TLS_PUBKEY, + TXT_TLS_CERT_HEADER, + TXT_TLS_CERT_SUBJECT_HEADER, + TXT_TLS_CERT_ISSUER_HEADER, + TXT_TLS_CERT_NAMED_ENTRY, + TXT_TLS_PUBKEY_FINGERPRINT, + TXT_TLS_CERT_FINGERPRINT, + TXT_TLS_PROTOCOL_VERSION }; extern FORMAT_REC fecommon_core_formats[]; From 5146ce96318bc9a2513d104ea76ab3277a540074 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 14:39:00 +0200 Subject: [PATCH 10/19] Add x509 certificate and public key pinning support. This patch adds two new options to /CONNECT and /SERVER to let the user pin either an x509 certificate and/or the public key of a given server. It is possible to fetch the certificate outside of Irssi itself to verify the checksum. To fetch the certificate call: $ openssl s_client -connect chat.freenode.net:6697 < /dev/null 2>/dev/null | \ openssl x509 > freenode.cert This will download chat.freenode.net:6697's TLS certificate and put it into the file freenode.cert. -tls_pinned_cert ---------------- This option allows you to specify the SHA-256 hash of the x509 certificate. When succesfully connected to the server, irssi will verify that the given server certificate matches the pin set by the user. The SHA-256 hash of a given certificate can be verified outside of irssi using the OpenSSL command line tool: $ openssl x509 -in freenode.cert -fingerprint -sha256 -noout -tls_pinned_pubkey ------------------ This option allows you to specify the SHA-256 hash of the subject public key information section of the server certificate. This section contains both the cryptographic parameters for the public key, but also information about the algorithm used together with the public key parameters. When succesfully connected to the server, irssi will verify that the given public key matches the pin set by the user. The SHA-256 hash of a public key can be verified outside of irssi using the OpenSSL command line tool: $ openssl x509 -in freenode.cert -pubkey -noout | \ openssl pkey -pubin -outform der | \ openssl dgst -sha256 -c | \ tr a-z A-Z It is possible to specify both -tls_pinned_cert and -tls_pinned_pubkey together. --- NEWS | 21 +++++++++++++++++++++ docs/help/in/connect.in | 2 ++ docs/help/in/server.in | 2 ++ src/core/chat-commands.c | 6 +++++- src/core/network-openssl.c | 20 ++++++++++++++++++++ src/core/server-connect-rec.h | 2 ++ src/core/server-setup-rec.h | 2 ++ src/core/servers-reconnect.c | 2 ++ src/core/servers-setup.c | 18 ++++++++++++++++++ src/core/servers.c | 2 ++ src/core/session.c | 2 ++ src/fe-common/core/fe-server.c | 15 +++++++++++++-- src/fe-common/irc/fe-irc-server.c | 4 ++++ 13 files changed, 95 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 4dad1712..e4431839 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,27 @@ v0.8.21-head 2016-xx-xx The Irssi team configuration. + Display TLS connection information upon connect. You can disable this by setting tls_verbose_connect to FALSE. + + Add -tls_pinned_cert and -tls_pinned_pubkey for x509 and public key pinning. + + The values needed for -tls_pinned_cert and -tls_pinned_pubkey is shown + when connecting to a TLS enabled IRC server, but you can also find the + values like this: Start by downloading the certificate from a given IRC + server: + + $ openssl s_client -connect chat.freenode.net:6697 < /dev/null 2>/dev/null | \ + openssl x509 > freenode.cert + + Find the value for -tls_pinned_cert: + + $ openssl x509 -in freenode.cert -fingerprint -sha256 -noout + + Find the value for -tls_pinned_pubkey: + + $ openssl x509 -in freenode.cert -pubkey -noout | \ + openssl pkey -pubin -outform der | \ + openssl dgst -sha256 -c | \ + tr a-z A-Z + - IP addresses are no longer stored when resolve_reverse_lookup is used. - /names and $[...] now uses utf8 string operations (#40, #411). diff --git a/docs/help/in/connect.in b/docs/help/in/connect.in index a0d793d2..e861ad74 100644 --- a/docs/help/in/connect.in +++ b/docs/help/in/connect.in @@ -15,6 +15,8 @@ -tls_cafile: The file with the list of CA certificates. -tls_capath: The directory which contains the CA certificates. -tls_ciphers: TLS cipher suite preference lists. + -tls_pinned_cert: Pinned x509 certificate fingerprint. + -tls_pinned_pubkey: Pinned public key fingerprint. -noproxy: Ignores the global proxy configuration. -network: The network this connection belongs to. -host: The hostname you would like to connect from. diff --git a/docs/help/in/server.in b/docs/help/in/server.in index ee1a30e1..60870111 100644 --- a/docs/help/in/server.in +++ b/docs/help/in/server.in @@ -24,6 +24,8 @@ -tls_cafile: The file with the list of CA certificates. -tls_capath: The directory which contains the CA certificates. -tls_ciphers: TLS cipher suite preference lists. + -tls_pinned_cert: Pinned x509 certificate fingerprint. + -tls_pinned_pubkey: Pinned public key fingerprint. -auto: Automatically connects to the server on startup. -noauto: Doesn't connect to the server on startup. -network: The network the server belongs to. diff --git a/src/core/chat-commands.c b/src/core/chat-commands.c index db60e46f..c737b810 100644 --- a/src/core/chat-commands.c +++ b/src/core/chat-commands.c @@ -115,6 +115,10 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr, conn->tls_capath = g_strdup(tmp); if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL) conn->tls_ciphers = g_strdup(tmp); + if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL) + conn->tls_pinned_cert = g_strdup(tmp); + if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL) + conn->tls_pinned_pubkey = g_strdup(tmp); if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0') || (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0')) conn->tls_verify = TRUE; @@ -494,7 +498,7 @@ void chat_commands_init(void) signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server); signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg); - command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +host noproxy -rawlog noautosendcmd"); + command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_pinned_cert +ssl_pinned_pubkey tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd"); command_set_options("msg", "channel nick"); } diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index c221624f..13db6e25 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -753,6 +753,8 @@ int irssi_ssl_handshake(GIOChannel *handle) unsigned int pubkey_fingerprint_size; unsigned char cert_fingerprint[EVP_MAX_MD_SIZE]; unsigned int cert_fingerprint_size; + const char *pinned_cert_fingerprint = chan->server->connrec->tls_pinned_cert; + const char *pinned_pubkey_fingerprint = chan->server->connrec->tls_pinned_pubkey; TLS_REC *tls = NULL; ERR_clear_error(); @@ -814,6 +816,24 @@ int irssi_ssl_handshake(GIOChannel *handle) ret = 1; do { + if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') { + ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0; + + if (! ret) { + g_warning(" Pinned certificate mismatch"); + continue; + } + } + + if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') { + ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0; + + if (! ret) { + g_warning(" Pinned public key mismatch"); + continue; + } + } + if (chan->verify) { ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls); diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h index 35577fd4..fa348769 100644 --- a/src/core/server-connect-rec.h +++ b/src/core/server-connect-rec.h @@ -29,6 +29,8 @@ char *tls_pass; char *tls_cafile; char *tls_capath; char *tls_ciphers; +char *tls_pinned_cert; +char *tls_pinned_pubkey; GIOChannel *connect_handle; /* connect using this handle */ diff --git a/src/core/server-setup-rec.h b/src/core/server-setup-rec.h index 22876d4e..e6b0431c 100644 --- a/src/core/server-setup-rec.h +++ b/src/core/server-setup-rec.h @@ -17,6 +17,8 @@ char *tls_pass; char *tls_cafile; char *tls_capath; char *tls_ciphers; +char *tls_pinned_cert; +char *tls_pinned_pubkey; char *own_host; /* address to use when connecting this server */ IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */ diff --git a/src/core/servers-reconnect.c b/src/core/servers-reconnect.c index 16ec1fac..1727704c 100644 --- a/src/core/servers-reconnect.c +++ b/src/core/servers-reconnect.c @@ -199,6 +199,8 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info) dest->tls_cafile = g_strdup(src->tls_cafile); dest->tls_capath = g_strdup(src->tls_capath); dest->tls_ciphers = g_strdup(src->tls_ciphers); + dest->tls_pinned_cert = g_strdup(src->tls_pinned_cert); + dest->tls_pinned_pubkey = g_strdup(src->tls_pinned_pubkey); return dest; } diff --git a/src/core/servers-setup.c b/src/core/servers-setup.c index 01a36e1c..9492c58c 100644 --- a/src/core/servers-setup.c +++ b/src/core/servers-setup.c @@ -181,6 +181,10 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn, conn->tls_capath = g_strdup(sserver->tls_capath); if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0') conn->tls_ciphers = g_strdup(sserver->tls_ciphers); + if (conn->tls_pinned_cert == NULL && sserver->tls_pinned_cert != NULL && sserver->tls_pinned_cert[0] != '\0') + conn->tls_pinned_cert = g_strdup(sserver->tls_pinned_cert); + if (conn->tls_pinned_pubkey == NULL && sserver->tls_pinned_pubkey != NULL && sserver->tls_pinned_pubkey[0] != '\0') + conn->tls_pinned_pubkey = g_strdup(sserver->tls_pinned_pubkey); server_setup_fill_reconn(conn, sserver); @@ -435,6 +439,16 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node) value = config_node_get_str(node, "ssl_ciphers", NULL); rec->tls_ciphers = g_strdup(value); + value = config_node_get_str(node, "tls_pinned_cert", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_pinned_cert", NULL); + rec->tls_pinned_cert = g_strdup(value); + + value = config_node_get_str(node, "tls_pinned_pubkey", NULL); + if (value == NULL) + value = config_node_get_str(node, "ssl_pinned_pubkey", NULL); + rec->tls_pinned_pubkey = g_strdup(value); + if (rec->tls_cafile || rec->tls_capath) rec->tls_verify = TRUE; if (rec->tls_cert != NULL || rec->tls_verify) @@ -500,6 +514,8 @@ static void server_setup_save(SERVER_SETUP_REC *rec) iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile); iconfig_node_set_str(node, "tls_capath", rec->tls_capath); iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers); + iconfig_node_set_str(node, "tls_pinned_cert", rec->tls_pinned_cert); + iconfig_node_set_str(node, "tls_pinned_pubkey", rec->tls_pinned_pubkey); iconfig_node_set_str(node, "own_host", rec->own_host); @@ -550,6 +566,8 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec) g_free_not_null(rec->tls_cafile); g_free_not_null(rec->tls_capath); g_free_not_null(rec->tls_ciphers); + g_free_not_null(rec->tls_pinned_cert); + g_free_not_null(rec->tls_pinned_pubkey); g_free(rec->address); g_free(rec); } diff --git a/src/core/servers.c b/src/core/servers.c index 2a14d510..b9faab81 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -633,6 +633,8 @@ void server_connect_unref(SERVER_CONNECT_REC *conn) g_free_not_null(conn->tls_cafile); g_free_not_null(conn->tls_capath); g_free_not_null(conn->tls_ciphers); + g_free_not_null(conn->tls_pinned_cert); + g_free_not_null(conn->tls_pinned_pubkey); g_free_not_null(conn->channels); g_free_not_null(conn->away_reason); diff --git a/src/core/session.c b/src/core/session.c index 5b3303bb..34190c52 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -165,6 +165,8 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config, config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile); config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath); config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers); + config_node_set_str(config, node, "tls_pinned_cert", server->connrec->tls_pinned_cert); + config_node_set_str(config, node, "tls_pinned_pubkey", server->connrec->tls_pinned_pubkey); handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle)); config_node_set_int(config, node, "handle", handle); diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c index b9522bc1..f4c1d3ee 100644 --- a/src/fe-common/core/fe-server.c +++ b/src/fe-common/core/fe-server.c @@ -196,6 +196,17 @@ static void cmd_server_add_modify(const char *data, gboolean add) if (value != NULL && *value != '\0') rec->tls_ciphers = g_strdup(value); + value = g_hash_table_lookup(optlist, "tls_pinned_cert"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pinned_cert"); + if (value != NULL && *value != '\0') + rec->tls_pinned_cert = g_strdup(value); + + value = g_hash_table_lookup(optlist, "tls_pinned_pubkey"); + if (value == NULL) + value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey"); + if (value != NULL && *value != '\0') + rec->tls_pinned_pubkey = g_strdup(value); if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0') || (rec->tls_capath != NULL && rec->tls_capath[0] != '\0')) @@ -423,8 +434,8 @@ void fe_server_init(void) command_bind_first("server", NULL, (SIGNAL_FUNC) server_command); command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command); - command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); - command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers auto noauto proxy noproxy -host -port noautosendcmd"); + command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd"); + command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd"); signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting); diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c index 36ed2bdc..c4435d8f 100644 --- a/src/fe-common/irc/fe-irc-server.c +++ b/src/fe-common/irc/fe-irc-server.c @@ -125,6 +125,10 @@ static void cmd_server_list(const char *data) g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath); if (rec->tls_ciphers) g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers); + if (rec->tls_pinned_cert) + g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert); + if (rec->tls_pinned_pubkey) + g_string_append_printf(str, "tls_pinned_pubkey: %s, ", rec->tls_pinned_pubkey); } if (rec->max_cmds_at_once > 0) From 53d772e48b16e34a82bd9b051f02252f900bb8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 15:04:55 +0200 Subject: [PATCH 11/19] Make sure we clean-up after ourself upon failure. --- src/core/network-openssl.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 13db6e25..3be33b2f 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -787,18 +787,20 @@ int irssi_ssl_handshake(GIOChannel *handle) if (cert == NULL) { g_warning("TLS server supplied no certificate"); - return -1; + ret = 0; + goto done; } if (pubkey == NULL) { g_warning("TLS server supplied no certificate public key"); - return -1; + ret = 0; + goto done; } if (! X509_digest(cert, EVP_sha256(), cert_fingerprint, &cert_fingerprint_size)) { g_warning("Unable to generate certificate fingerprint"); - X509_free(cert); - return -1; + ret = 0; + goto done; } pubkey_size = i2d_X509_PUBKEY(pubkey, NULL); @@ -847,6 +849,7 @@ int irssi_ssl_handshake(GIOChannel *handle) // Emit the TLS rec. signal_emit("tls handshake finished", 2, chan->server, tls); +done: tls_rec_free(tls); X509_free(cert); g_free(pubkey_der); From 4e170c52335d06456aba65b46842aa98f8aabe7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 16 Oct 2016 15:18:47 +0200 Subject: [PATCH 12/19] Declare variables in the beginning of the function. --- src/core/network-openssl.c | 32 +++++++++++++------------------- src/fe-common/core/fe-tls.c | 16 +++++++++------- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 3be33b2f..428881df 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -615,8 +615,16 @@ static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) g_return_if_fail(tls != NULL); g_return_if_fail(ssl != NULL); + int nid; + char *key = NULL; + char *value = NULL; STACK_OF(X509) *chain = NULL; int i; + int j; + TLS_CERT_REC *cert_rec = NULL; + X509_NAME *name = NULL; + X509_NAME_ENTRY *entry = NULL; + TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL; chain = SSL_get_peer_cert_chain(ssl); @@ -624,24 +632,12 @@ static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) return; for (i = 0; i < sk_X509_num(chain); i++) { - TLS_CERT_REC *cert_rec = NULL; - X509_NAME *name = NULL; - - int j; - int nid; - - char *key = NULL; - char *value = NULL; - cert_rec = tls_cert_create_rec(); // Subject. name = X509_get_subject_name(sk_X509_value(chain, i)); for (j = 0; j < X509_NAME_entry_count(name); j++) { - X509_NAME_ENTRY *entry = NULL; - TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL; - entry = X509_NAME_get_entry(name, j); nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); @@ -661,9 +657,6 @@ static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) name = X509_get_issuer_name(sk_X509_value(chain, i)); for (j = 0; j < X509_NAME_entry_count(name); j++) { - X509_NAME_ENTRY *entry = NULL; - TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL; - entry = X509_NAME_get_entry(name, j); nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(entry)); @@ -691,6 +684,9 @@ static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl) #ifdef SSL_get_server_tmp_key // Show ephemeral key information. EVP_PKEY *ephemeral_key = NULL; + char *ephemeral_key_algorithm = NULL; + char *cname = NULL; + int nid; if (SSL_get_server_tmp_key(ssl, &ephemeral_key)) { switch (EVP_PKEY_id(ephemeral_key)) { @@ -702,12 +698,10 @@ static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl) case EVP_PKEY_EC: { EC_KEY *ec = EVP_PKEY_get1_EC_KEY(ephemeral_key); - int nid; - const char *cname; nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); EC_KEY_free(ec); - cname = OBJ_nid2sn(nid); - char *ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname); + cname = (char *)OBJ_nid2sn(nid); + ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname); tls_rec_set_ephemeral_key_algorithm(tls, ephemeral_key_algorithm); tls_rec_set_ephemeral_key_size(tls, EVP_PKEY_bits(ephemeral_key)); diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c index ce9bddbf..7a7c537a 100644 --- a/src/fe-common/core/fe-tls.c +++ b/src/fe-common/core/fe-tls.c @@ -29,29 +29,31 @@ static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls) { + GSList *certs = NULL; + GSList *subject = NULL; + GSList *issuer = NULL; + TLS_CERT_ENTRY_REC *data = NULL; + if (! settings_get_bool("tls_verbose_connect")) return; printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER); - GSList *certs = NULL; for (certs = tls->certs; certs != NULL; certs = certs->next) { TLS_CERT_REC *tls_cert_rec = certs->data; printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT_HEADER); - GSList *subject = NULL; for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) { - TLS_CERT_ENTRY_REC *subject_data = subject->data; - printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, subject_data->name, subject_data->value); + data = subject->data; + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, data->name, data->value); } printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER_HEADER); - GSList *issuer = NULL; for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) { - TLS_CERT_ENTRY_REC *issuer_data = issuer->data; - printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, issuer_data->name, issuer_data->value); + data = issuer->data; + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, data->name, data->value); } } From 25824e2d3fb42def5efbb59ff34a51e31af04ec8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Wed, 19 Oct 2016 23:37:04 +0200 Subject: [PATCH 13/19] Lift ASN1_STRING declaration onto the entry of the function. --- src/core/network-openssl.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 428881df..8c258eca 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -625,6 +625,7 @@ static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) X509_NAME *name = NULL; X509_NAME_ENTRY *entry = NULL; TLS_CERT_ENTRY_REC *tls_cert_entry_rec = NULL; + ASN1_STRING *data = NULL; chain = SSL_get_peer_cert_chain(ssl); @@ -646,8 +647,8 @@ static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) if (key == NULL) key = (char *)OBJ_nid2ln(nid); - ASN1_STRING *d = X509_NAME_ENTRY_get_data(entry); - value = (char *)ASN1_STRING_data(d); + data = X509_NAME_ENTRY_get_data(entry); + value = (char *)ASN1_STRING_data(data); tls_cert_entry_rec = tls_cert_entry_create_rec(key, value); tls_cert_rec_append_subject_entry(cert_rec, tls_cert_entry_rec); @@ -665,8 +666,8 @@ static void set_peer_cert_chain_info(TLS_REC *tls, SSL *ssl) if (key == NULL) key = (char *)OBJ_nid2ln(nid); - ASN1_STRING *d = X509_NAME_ENTRY_get_data(entry); - value = (char *)ASN1_STRING_data(d); + data = X509_NAME_ENTRY_get_data(entry); + value = (char *)ASN1_STRING_data(data); tls_cert_entry_rec = tls_cert_entry_create_rec(key, value); tls_cert_rec_append_issuer_entry(cert_rec, tls_cert_entry_rec); From f533baa191428b3cbdbba151d575b69e3a783f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Wed, 19 Oct 2016 23:38:21 +0200 Subject: [PATCH 14/19] Lift EC_KEY declaration onto the entry of the function. --- src/core/network-openssl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 8c258eca..4c6b75dd 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -685,6 +685,7 @@ static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl) #ifdef SSL_get_server_tmp_key // Show ephemeral key information. EVP_PKEY *ephemeral_key = NULL; + EC_KEY *ec_key = NULL; char *ephemeral_key_algorithm = NULL; char *cname = NULL; int nid; @@ -697,10 +698,9 @@ static void set_server_temporary_key_info(TLS_REC *tls, SSL *ssl) break; case EVP_PKEY_EC: - { - EC_KEY *ec = EVP_PKEY_get1_EC_KEY(ephemeral_key); - nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec)); - EC_KEY_free(ec); + ec_key = EVP_PKEY_get1_EC_KEY(ephemeral_key); + nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); + EC_KEY_free(ec_key); cname = (char *)OBJ_nid2sn(nid); ephemeral_key_algorithm = g_strdup_printf("ECDH: %s", cname); From 5a04430998ada5ae800aa0a88638206de51287ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Fri, 21 Oct 2016 01:17:35 +0200 Subject: [PATCH 15/19] Kill support for DANE. This patch removes support for DANE validation of TLS certificates. There wasn't enough support in the IRC community to push for this on the majority of bigger IRC networks. If you believe this should be reintroduced into irssi, then please come up with an implementation that does not rely on the libval library. It is causing a lot of troubles for our downstream maintainers. --- NEWS | 8 ++++++++ configure.ac | 21 -------------------- docs/signals.txt | 3 --- src/core/network-openssl.c | 39 -------------------------------------- 4 files changed, 8 insertions(+), 63 deletions(-) diff --git a/NEWS b/NEWS index e4431839..1709fbc3 100644 --- a/NEWS +++ b/NEWS @@ -46,6 +46,14 @@ v0.8.21-head 2016-xx-xx The Irssi team openssl dgst -sha256 -c | \ tr a-z A-Z + + Remove support for DANE validation of TLS certificates. + + There wasn't enough support in the IRC community to push for this on the + majority of bigger IRC networks. If you believe this should be + reintroduced into irssi, then please come up with an implementation that + does not rely on the libval library. It is causing a lot of troubles for + our downstream maintainers. + - IP addresses are no longer stored when resolve_reverse_lookup is used. - /names and $[...] now uses utf8 string operations (#40, #411). diff --git a/configure.ac b/configure.ac index 629dd590..32a3ebfd 100644 --- a/configure.ac +++ b/configure.ac @@ -135,15 +135,6 @@ AC_ARG_WITH(perl, fi, want_perl=static) -AC_ARG_ENABLE(dane, -[ --enable-dane Enable DANE support], - if test x$enableval = xno ; then - want_dane=no - else - want_dane=yes - fi, - want_dane=no) - AC_ARG_ENABLE(true-color, [ --enable-true-color Build with true color support in terminal], if test x$enableval = xno ; then @@ -537,17 +528,6 @@ COMMON_LIBS="$FE_COMMON_LIBS $COMMON_NOUI_LIBS" AC_SUBST(COMMON_NOUI_LIBS) AC_SUBST(COMMON_LIBS) -have_dane=no -if test "x$want_dane" = "xyes"; then - AC_MSG_CHECKING([for DANE]) - AC_CHECK_LIB(val-threads, val_getdaneinfo, - [ - LIBS="$LIBS -lval-threads -lsres" - AC_DEFINE([HAVE_DANE], [], [DANE support]) - have_dane=yes - ], [], [-lssl -lcrypto -lsres -lpthread]) -fi - if test "x$want_truecolor" = "xyes"; then AC_DEFINE([TERM_TRUECOLOR], [], [true color support in terminal]) else @@ -667,7 +647,6 @@ echo "Install prefix ................... : $prefix" echo echo "Building with 64bit DCC support .. : $offt_64bit" -echo "Building with DANE support ....... : $have_dane" echo "Building with true color support.. : $want_truecolor" echo diff --git a/docs/signals.txt b/docs/signals.txt index 47db3575..7776dad7 100644 --- a/docs/signals.txt +++ b/docs/signals.txt @@ -56,9 +56,6 @@ modules.c: "module error", int error, char *text, char *rootmodule, char *submodule network-openssl.c: - "tlsa available", SERVER_REC - "tlsa verification success", SERVER_REC - "tlsa verification failed", SERVER_REC "tls handshake finished", SERVER_REC, TLS_REC nicklist.c: diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index 4c6b75dd..e28c8c14 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -32,11 +32,6 @@ #include #include -#ifdef HAVE_DANE -#include -#include -#endif - /* ssl i/o channel object */ typedef struct { @@ -207,40 +202,6 @@ static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, i { long result; -#ifdef HAVE_DANE - int dane_ret; - struct val_daneparams daneparams; - struct val_danestatus *danestatus = NULL; - - // Check if a TLSA record is available. - daneparams.port = port; - daneparams.proto = DANE_PARAM_PROTO_TCP; - - dane_ret = val_getdaneinfo(NULL, hostname, &daneparams, &danestatus); - - if (dane_ret == VAL_DANE_NOERROR) { - signal_emit("tlsa available", 1, server); - } - - if (danestatus != NULL) { - int do_certificate_check = 1; - - if (val_dane_check(NULL, ssl, danestatus, &do_certificate_check) != VAL_DANE_NOERROR) { - g_warning("DANE: TLSA record for hostname %s port %d could not be verified", hostname, port); - signal_emit("tlsa verification failed", 1, server); - val_free_dane(danestatus); - return FALSE; - } - - signal_emit("tlsa verification success", 1, server); - val_free_dane(danestatus); - - if (do_certificate_check == 0) { - return TRUE; - } - } -#endif - result = SSL_get_verify_result(ssl); if (result != X509_V_OK) { g_warning("Could not verify TLS servers certificate: %s", X509_verify_cert_error_string(result)); From d501a54f4f256d55ae0a3cd5272d5bb8a42daee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Fri, 21 Oct 2016 01:22:09 +0200 Subject: [PATCH 16/19] Emit the TLS handshake finished signal before we do verification. This patch moves the emitted "tls handshake finished" signal to before we do validation of the given TLS certificate. This ensures that we display certificate information before we possibly error out and disconnects from the server. --- src/core/network-openssl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index e28c8c14..df84772c 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -771,6 +771,9 @@ int irssi_ssl_handshake(GIOChannel *handle) set_peer_cert_chain_info(tls, chan->ssl); set_server_temporary_key_info(tls, chan->ssl); + // Emit the TLS rec. + signal_emit("tls handshake finished", 2, chan->server, tls); + ret = 1; do { @@ -802,9 +805,6 @@ int irssi_ssl_handshake(GIOChannel *handle) } } while (0); - // Emit the TLS rec. - signal_emit("tls handshake finished", 2, chan->server, tls); - done: tls_rec_free(tls); X509_free(cert); From 0a6e66f8b794de76bd48bda199977b977a377e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Fri, 21 Oct 2016 17:56:29 +0200 Subject: [PATCH 17/19] Kill do { ... } while (0); and replace it with goto's. --- src/core/network-openssl.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index df84772c..7a1d6e34 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -776,34 +776,32 @@ int irssi_ssl_handshake(GIOChannel *handle) ret = 1; - do { - if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') { - ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0; + if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') { + ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0; - if (! ret) { - g_warning(" Pinned certificate mismatch"); - continue; - } + if (! ret) { + g_warning(" Pinned certificate mismatch"); + goto done; } + } - if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') { - ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0; + if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') { + ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0; - if (! ret) { - g_warning(" Pinned public key mismatch"); - continue; - } + if (! ret) { + g_warning(" Pinned public key mismatch"); + goto done; } + } - if (chan->verify) { - ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls); + if (chan->verify) { + ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls); - if (! ret) { - // irssi_ssl_verify emits a warning itself. - continue; - } + if (! ret) { + // irssi_ssl_verify emits a warning itself. + goto done; } - } while (0); + } done: tls_rec_free(tls); From 322625b5487527420cae0a6a1c0da8ecdc87fd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Fri, 21 Oct 2016 17:58:28 +0200 Subject: [PATCH 18/19] Only do checks for SSL_get_server_tmp_key in network-openssl.c. --- src/core/tls.c | 5 ----- src/core/tls.h | 5 ----- src/fe-common/core/fe-tls.c | 2 -- 3 files changed, 12 deletions(-) diff --git a/src/core/tls.c b/src/core/tls.c index 8aad0c89..3bddd773 100644 --- a/src/core/tls.c +++ b/src/core/tls.c @@ -42,10 +42,7 @@ void tls_rec_free(TLS_REC *tls_rec) g_free_and_null(tls_rec->certificate_fingerprint_algorithm); g_free_and_null(tls_rec->not_after); g_free_and_null(tls_rec->not_before); - -#ifdef SSL_get_server_tmp_key g_free_and_null(tls_rec->ephemeral_key_algorithm); -#endif if (tls_rec->certs != NULL) { g_slist_foreach(tls_rec->certs, (GFunc)tls_cert_rec_free, NULL); @@ -130,7 +127,6 @@ void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before) tls_rec->not_before = g_strdup(not_before); } -#ifdef SSL_get_server_tmp_key void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm) { g_return_if_fail(tls_rec != NULL); @@ -142,7 +138,6 @@ void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size) g_return_if_fail(tls_rec != NULL); tls_rec->ephemeral_key_size = size; } -#endif void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec) { diff --git a/src/core/tls.h b/src/core/tls.h index 4991f7ed..9ba4ac47 100644 --- a/src/core/tls.h +++ b/src/core/tls.h @@ -43,10 +43,8 @@ struct _TLS_REC { char *not_after; char *not_before; -#ifdef SSL_get_server_tmp_key char *ephemeral_key_algorithm; size_t ephemeral_key_size; -#endif GSList *certs; }; @@ -75,11 +73,8 @@ void tls_rec_set_certificate_fingerprint(TLS_REC *tls_rec, const char *fingerpri void tls_rec_set_certificate_fingerprint_algorithm(TLS_REC *tls_rec, const char *algorithm); void tls_rec_set_not_after(TLS_REC *tls_rec, const char *not_after); void tls_rec_set_not_before(TLS_REC *tls_rec, const char *not_before); - -#ifdef SSL_get_server_tmp_key void tls_rec_set_ephemeral_key_algorithm(TLS_REC *tls_rec, const char *algorithm); void tls_rec_set_ephemeral_key_size(TLS_REC *tls_rec, size_t size); -#endif void tls_rec_append_cert(TLS_REC *tls_rec, TLS_CERT_REC *tls_cert_rec); diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c index 7a7c537a..608666f0 100644 --- a/src/fe-common/core/fe-tls.c +++ b/src/fe-common/core/fe-tls.c @@ -59,12 +59,10 @@ static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls) printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher); -#ifdef SSL_get_server_tmp_key if (tls->ephemeral_key_algorithm != NULL) printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY, tls->ephemeral_key_size, tls->ephemeral_key_algorithm); else printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE); -#endif printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY, tls->public_key_size, tls->public_key_algorithm, tls->not_before, tls->not_after); printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PUBKEY_FINGERPRINT, tls->public_key_fingerprint, tls->public_key_fingerprint_algorithm); From bc4e2c9ade41182ff643a94bbcb72ffe86b0e67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20F=C3=A6r=C3=B8y?= Date: Sun, 23 Oct 2016 02:51:08 +0200 Subject: [PATCH 19/19] Shorten the certificate chain output. --- src/fe-common/core/fe-tls.c | 22 +++++++++++++++++----- src/fe-common/core/module-formats.c | 5 ++--- src/fe-common/core/module-formats.h | 5 ++--- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/fe-common/core/fe-tls.c b/src/fe-common/core/fe-tls.c index 608666f0..ed206d18 100644 --- a/src/fe-common/core/fe-tls.c +++ b/src/fe-common/core/fe-tls.c @@ -32,6 +32,7 @@ static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls) GSList *certs = NULL; GSList *subject = NULL; GSList *issuer = NULL; + GString *str = NULL; TLS_CERT_ENTRY_REC *data = NULL; if (! settings_get_bool("tls_verbose_connect")) @@ -41,20 +42,31 @@ static void tls_handshake_finished(SERVER_REC *server, TLS_REC *tls) for (certs = tls->certs; certs != NULL; certs = certs->next) { TLS_CERT_REC *tls_cert_rec = certs->data; - - printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT_HEADER); + str = g_string_new(NULL); for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) { data = subject->data; - printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, data->name, data->value); + g_string_append_printf(str, "%s: %s, ", data->name, data->value); } - printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER_HEADER); + if (str->len > 1) + g_string_truncate(str, str->len - 2); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_SUBJECT, str->str); + g_string_free(str, TRUE); + + str = g_string_new(NULL); for (issuer = tls_cert_rec->issuer; issuer != NULL; issuer = issuer->next) { data = issuer->data; - printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_NAMED_ENTRY, data->name, data->value); + g_string_append_printf(str, "%s: %s, ", data->name, data->value); } + + if (str->len > 1) + g_string_truncate(str, str->len - 2); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_ISSUER, str->str); + g_string_free(str, TRUE); } printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_PROTOCOL_VERSION, tls->protocol_version, tls->cipher_size, tls->cipher); diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c index b4747d16..da9705be 100644 --- a/src/fe-common/core/module-formats.c +++ b/src/fe-common/core/module-formats.c @@ -298,9 +298,8 @@ FORMAT_REC fecommon_core_formats[] = { { "tls_ephemeral_key_unavailable", "EDH Key: {error N/A}", 0 }, { "tls_pubkey", "Public Key: {hilight $0} bit {hilight $1}, valid from {hilight $2} to {hilight $3}", 4, { 1, 0, 0, 0 } }, { "tls_cert_header", "Certificate Chain:", 0 }, - { "tls_cert_subject_header", " Subject:", 0 }, - { "tls_cert_issuer_header", " Issuer:", 0 }, - { "tls_cert_named_entry", " $[-2]0: {hilight $1}", 2, { 0, 0 } }, + { "tls_cert_subject", " Subject: {hilight $0}", 1, { 0 } }, + { "tls_cert_issuer", " Issuer: {hilight $0}", 1, { 0 } }, { "tls_pubkey_fingerprint", "Public Key Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } }, { "tls_cert_fingerprint", "Certificate Fingerprint: {hilight $0} ({hilight $1})", 2, { 0, 0 } }, { "tls_protocol_version", "Protocol: {hilight $0} ({hilight $1} bit, {hilight $2})", 3, { 0, 1, 0 } }, diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h index 4cdeb407..a9ed28c5 100644 --- a/src/fe-common/core/module-formats.h +++ b/src/fe-common/core/module-formats.h @@ -262,9 +262,8 @@ enum { TXT_TLS_EPHEMERAL_KEY_UNAVAILBLE, TXT_TLS_PUBKEY, TXT_TLS_CERT_HEADER, - TXT_TLS_CERT_SUBJECT_HEADER, - TXT_TLS_CERT_ISSUER_HEADER, - TXT_TLS_CERT_NAMED_ENTRY, + TXT_TLS_CERT_SUBJECT, + TXT_TLS_CERT_ISSUER, TXT_TLS_PUBKEY_FINGERPRINT, TXT_TLS_CERT_FINGERPRINT, TXT_TLS_PROTOCOL_VERSION