summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS37
-rw-r--r--configure.ac72
-rw-r--r--docs/help/in/connect.in35
-rw-r--r--docs/help/in/server.in66
-rw-r--r--docs/signals.txt4
-rw-r--r--irssi.conf12
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/chat-commands.c48
-rw-r--r--src/core/misc.c20
-rw-r--r--src/core/misc.h4
-rw-r--r--src/core/network-openssl.c378
-rw-r--r--src/core/server-connect-rec.h18
-rw-r--r--src/core/server-setup-rec.h18
-rw-r--r--src/core/servers-reconnect.c16
-rw-r--r--src/core/servers-setup.c132
-rw-r--r--src/core/servers.c34
-rw-r--r--src/core/session.c19
-rw-r--r--src/core/tls.c214
-rw-r--r--src/core/tls.h90
-rw-r--r--src/fe-common/core/Makefile.am2
-rw-r--r--src/fe-common/core/fe-common-core.c5
-rw-r--r--src/fe-common/core/fe-server.c71
-rw-r--r--src/fe-common/core/fe-tls.c94
-rw-r--r--src/fe-common/core/fe-tls.h25
-rw-r--r--src/fe-common/core/module-formats.c13
-rw-r--r--src/fe-common/core/module-formats.h14
-rw-r--r--src/fe-common/irc/fe-irc-server.c34
-rw-r--r--src/irc/core/irc-servers.c4
-rwxr-xr-xsrc/perl/get-signals.pl1
-rw-r--r--src/perl/perl-common.c3
30 files changed, 1106 insertions, 379 deletions
diff --git a/NEWS b/NEWS
index a76771e7..1709fbc3 100644
--- a/NEWS
+++ b/NEWS
@@ -17,6 +17,43 @@ v0.8.21-head 2016-xx-xx The Irssi team <staff@irssi.org>
+ 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.
+ + 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.
+ + 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
+
+ + 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 ee03eb82..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
@@ -154,14 +145,6 @@ AC_ARG_ENABLE(true-color,
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 **
@@ -239,6 +222,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 +267,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
@@ -560,23 +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])
-
- 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
AC_DEFINE([TERM_TRUECOLOR], [], [true color support in terminal])
else
@@ -695,18 +646,7 @@ 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"
echo
diff --git a/docs/help/in/connect.in b/docs/help/in/connect.in
index df50d1b9..e861ad74 100644
--- a/docs/help/in/connect.in
+++ b/docs/help/in/connect.in
@@ -5,23 +5,24 @@
%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.
+ -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.
+ -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..60870111 100644
--- a/docs/help/in/server.in
+++ b/docs/help/in/server.in
@@ -5,45 +5,47 @@
%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.
+ -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.
+ -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/docs/signals.txt b/docs/signals.txt
index 5e03d901..7776dad7 100644
--- a/docs/signals.txt
+++ b/docs/signals.txt
@@ -56,9 +56,7 @@ 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:
"nicklist new", CHANNEL_REC, NICK_REC
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"; }
);
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/chat-commands.c b/src/core/chat-commands.c
index a9404fa3..c737b810 100644
--- a/src/core/chat-commands.c
+++ b/src/core/chat-commands.c
@@ -99,27 +99,31 @@ 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 ((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;
+ 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 +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 +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/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
diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c
index a18e6fc7..7a1d6e34 100644
--- a/src/core/network-openssl.c
+++ b/src/core/network-openssl.c
@@ -22,8 +22,8 @@
#include "network.h"
#include "misc.h"
#include "servers.h"
-
-#ifdef HAVE_OPENSSL
+#include "signals.h"
+#include "tls.h"
#include <openssl/crypto.h>
#include <openssl/x509.h>
@@ -32,11 +32,6 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
-#ifdef HAVE_DANE
-#include <validator/validator.h>
-#include <validator/val_dane.h>
-#endif
-
/* ssl i/o channel object */
typedef struct
{
@@ -203,78 +198,13 @@ 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;
- 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) {
- 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;
@@ -457,13 +387,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);
@@ -482,7 +412,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");
}
@@ -511,7 +442,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);
@@ -565,6 +496,192 @@ 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);
+
+ 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;
+ ASN1_STRING *data = NULL;
+
+ chain = SSL_get_peer_cert_chain(ssl);
+
+ if (chain == NULL)
+ return;
+
+ for (i = 0; i < sk_X509_num(chain); i++) {
+ 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++) {
+ 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);
+
+ 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);
+ }
+
+ // Issuer.
+ name = X509_get_issuer_name(sk_X509_value(chain, i));
+
+ for (j = 0; j < X509_NAME_entry_count(name); j++) {
+ 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);
+
+ 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);
+ }
+
+ 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;
+ EC_KEY *ec_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)) {
+ 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 = 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);
+
+ 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;
@@ -582,8 +699,19 @@ 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;
+ 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();
ret = SSL_connect(chan->ssl);
@@ -611,22 +739,74 @@ 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;
+ g_warning("TLS server supplied no certificate");
+ ret = 0;
+ goto done;
}
- ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server);
- X509_free(cert);
- return ret ? 0 : -1;
-}
-#else /* HAVE_OPENSSL */
+ if (pubkey == NULL) {
+ g_warning("TLS server supplied no certificate public key");
+ ret = 0;
+ goto done;
+ }
-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;
-}
+ if (! X509_digest(cert, EVP_sha256(), cert_fingerprint, &cert_fingerprint_size)) {
+ g_warning("Unable to generate certificate fingerprint");
+ ret = 0;
+ goto done;
+ }
+
+ 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);
+
+ // Emit the TLS rec.
+ signal_emit("tls handshake finished", 2, chan->server, tls);
+
+ ret = 1;
+
+ 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");
+ goto done;
+ }
+ }
-#endif /* ! HAVE_OPENSSL */
+ 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");
+ goto done;
+ }
+ }
+
+ 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.
+ goto done;
+ }
+ }
+
+done:
+ tls_rec_free(tls);
+ X509_free(cert);
+ g_free(pubkey_der);
+
+ return ret ? 0 : -1;
+}
diff --git a/src/core/server-connect-rec.h b/src/core/server-connect-rec.h
index 80c5761b..fa348769 100644
--- a/src/core/server-connect-rec.h
+++ b/src/core/server-connect-rec.h
@@ -23,12 +23,14 @@ 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;
+char *tls_pinned_cert;
+char *tls_pinned_pubkey;
GIOChannel *connect_handle; /* connect using this handle */
@@ -38,8 +40,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..e6b0431c 100644
--- a/src/core/server-setup-rec.h
+++ b/src/core/server-setup-rec.h
@@ -11,12 +11,14 @@ 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 *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 */
@@ -28,7 +30,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..1727704c 100644
--- a/src/core/servers-reconnect.c
+++ b/src/core/servers-reconnect.c
@@ -192,13 +192,15 @@ 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);
+ 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 0cecfece..9492c58c 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -167,20 +167,24 @@ 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);
+ 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);
@@ -362,9 +366,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 +395,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 +405,55 @@ 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);
+
+ 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)
+ 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 +505,18 @@ 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, "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);
iconfig_node_set_str(node, "family",
@@ -514,12 +560,14 @@ 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_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 d6297c4d..b9faab81 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)
@@ -221,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);
@@ -239,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;
@@ -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)
+ if (server->connrec->use_tls)
server_connect_callback_init_ssl(server, handle);
else
-#endif
server->connect_tag =
g_input_add(handle, G_INPUT_WRITE | G_INPUT_READ,
(GInputFunction)
@@ -626,22 +622,24 @@ 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->tls_pinned_cert);
+ g_free_not_null(conn->tls_pinned_pubkey);
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..34190c52 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,15 @@ 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);
+ 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/core/tls.c b/src/core/tls.c
new file mode 100644
index 00000000..3bddd773
--- /dev/null
+++ b/src/core/tls.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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);
+ g_free_and_null(tls_rec->ephemeral_key_algorithm);
+
+ 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);
+}
+
+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;
+}
+
+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..9ba4ac47
--- /dev/null
+++ b/src/core/tls.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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 <openssl/ssl.h>
+
+#include <stdbool.h>
+
+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;
+
+ char *ephemeral_key_algorithm;
+ size_t ephemeral_key_size;
+
+ 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);
+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);
+
+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/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-server.c b/src/fe-common/core/fe-server.c
index 468cb707..f4c1d3ee 100644
--- a/src/fe-common/core/fe-server.c
+++ b/src/fe-common/core/fe-server.c
@@ -154,42 +154,66 @@ 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;
+ 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->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 +433,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 +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/core/fe-tls.c b/src/fe-common/core/fe-tls.c
new file mode 100644
index 00000000..ed206d18
--- /dev/null
+++ b/src/fe-common/core/fe-tls.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015 Alexander Færøy <ahf@irssi.org>
+ *
+ * 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)
+{
+ 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"))
+ return;
+
+ printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, TXT_TLS_CERT_HEADER);
+
+ for (certs = tls->certs; certs != NULL; certs = certs->next) {
+ TLS_CERT_REC *tls_cert_rec = certs->data;
+ str = g_string_new(NULL);
+
+ for (subject = tls_cert_rec->subject; subject != NULL; subject = subject->next) {
+ data = subject->data;
+ 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_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;
+ 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);
+
+ 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);
+
+ 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 <ahf@irssi.org>
+ *
+ * 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..da9705be 100644
--- a/src/fe-common/core/module-formats.c
+++ b/src/fe-common/core/module-formats.c
@@ -291,5 +291,18 @@ 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", " 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 } },
+
{ NULL, NULL, 0 }
};
diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h
index 2b45ff6b..a9ed28c5 100644
--- a/src/fe-common/core/module-formats.h
+++ b/src/fe-common/core/module-formats.h
@@ -254,7 +254,19 @@ 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,
+ TXT_TLS_CERT_ISSUER,
+ TXT_TLS_PUBKEY_FINGERPRINT,
+ TXT_TLS_CERT_FINGERPRINT,
+ TXT_TLS_PROTOCOL_VERSION
};
extern FORMAT_REC fecommon_core_formats[];
diff --git a/src/fe-common/irc/fe-irc-server.c b/src/fe-common/irc/fe-irc-server.c
index 2e22d6f2..c4435d8f 100644
--- a/src/fe-common/irc/fe-irc-server.c
+++ b/src/fe-common/irc/fe-irc-server.c
@@ -108,23 +108,27 @@ 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->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)
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/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 (<STDIN>) {
RAWLOG_REC => 'Irssi::Rawlog',
IGNORE_REC => 'Irssi::Ignore',
MODULE_REC => 'Irssi::Module',
+ TLS_REC => 'iobject',
# irc
BAN_REC => 'Irssi::Irc::Ban',
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);
}